[Libreoffice-commits] core.git: Branch 'private/swe/libreoffice-5-2+backports' - 6 commits - framework/Library_fwe.mk framework/source include/framework include/vcl sc/CppunitTest_sc_cond_format_merge.mk sc/inc sc/Module_sc.mk sc/qa sc/source sd/source uui/source vcl/source

Mike Kaganski mike.kaganski at collabora.com
Fri Dec 1 07:10:37 UTC 2017


Rebased ref, commits from common ancestor:
commit 7431394ad3a72ef960eb52f8718752f59e617567
Author: Mike Kaganski <mike.kaganski at collabora.com>
Date:   Sun Feb 12 01:58:23 2017 +0300

    tdf#76183: refresh objects' positions on optimal height recalc
    
    Since commit b10833d4db6046f2d32ea44a60cb19a626d80447, it's required
    to detect when objects' placement should be adjusted, and call
    SetDrawPageSize manually.
    Unit test included [not in this backport, though]
    
    Change-Id: I933ba4802b212400cc47ed0fb7e1f8f44049bb81
    Reviewed-on: https://gerrit.libreoffice.org/34165
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Mike Kaganski <mike.kaganski at collabora.com>
    Reviewed-on: https://gerrit.libreoffice.org/45570
    Reviewed-by: Thorsten Behrens <Thorsten.Behrens at CIB.de>
    Tested-by: Thorsten Behrens <Thorsten.Behrens at CIB.de>

diff --git a/sc/source/ui/docshell/docfunc.cxx b/sc/source/ui/docshell/docfunc.cxx
index e314e3b35273..c42f04e1bb31 100644
--- a/sc/source/ui/docshell/docfunc.cxx
+++ b/sc/source/ui/docshell/docfunc.cxx
@@ -151,6 +151,9 @@ bool ScDocFunc::AdjustRowHeight( const ScRange& rRange, bool bPaint )
 
     sc::RowHeightContext aCxt(aProv.GetPPTX(), aProv.GetPPTY(), aOne, aOne, aProv.GetDevice());
     bool bChanged = rDoc.SetOptimalHeight(aCxt, nStartRow, nEndRow, nTab);
+    // tdf#76183: recalculate objects' positions
+    if (bChanged)
+        rDoc.SetDrawPageSize(nTab);
 
     if ( bPaint && bChanged )
         rDocShell.PostPaint(ScRange(0, nStartRow, nTab, MAXCOL, MAXROW, nTab),
diff --git a/sc/source/ui/docshell/docsh5.cxx b/sc/source/ui/docshell/docsh5.cxx
index 32857f2b3dee..427dd9ab3525 100644
--- a/sc/source/ui/docshell/docsh5.cxx
+++ b/sc/source/ui/docshell/docsh5.cxx
@@ -394,7 +394,12 @@ bool ScDocShell::AdjustRowHeight( SCROW nStartRow, SCROW nEndRow, SCTAB nTab )
     bool bChange = aDocument.SetOptimalHeight(aCxt, nStartRow,nEndRow, nTab);
 
     if (bChange)
+    {
+        // tdf#76183: recalculate objects' positions
+        aDocument.SetDrawPageSize(nTab);
+
         PostPaint( 0,nStartRow,nTab, MAXCOL,MAXROW,nTab, PAINT_GRID|PAINT_LEFT );
+    }
 
     return bChange;
 }
diff --git a/sc/source/ui/undo/undobase.cxx b/sc/source/ui/undo/undobase.cxx
index 0133f9afd41a..9aa01565096b 100644
--- a/sc/source/ui/undo/undobase.cxx
+++ b/sc/source/ui/undo/undobase.cxx
@@ -308,10 +308,14 @@ bool ScBlockUndo::AdjustHeight()
         aCxt, aBlockRange.aStart.Row(), aBlockRange.aEnd.Row(), aBlockRange.aStart.Tab());
 
     if (bRet)
+    {
+        // tdf#76183: recalculate objects' positions
+        rDoc.SetDrawPageSize(aBlockRange.aStart.Tab());
+
         pDocShell->PostPaint( 0,      aBlockRange.aStart.Row(), aBlockRange.aStart.Tab(),
                               MAXCOL, MAXROW,                   aBlockRange.aEnd.Tab(),
                               PAINT_GRID | PAINT_LEFT );
-
+    }
     return bRet;
 }
 
@@ -408,9 +412,14 @@ void ScMultiBlockUndo::AdjustHeight()
         bool bRet = rDoc.SetOptimalHeight(aCxt, r.aStart.Row(), r.aEnd.Row(), r.aStart.Tab());
 
         if (bRet)
+        {
+            // tdf#76183: recalculate objects' positions
+            rDoc.SetDrawPageSize(r.aStart.Tab());
+
             pDocShell->PostPaint(
                 0, r.aStart.Row(), r.aStart.Tab(), MAXCOL, MAXROW, r.aEnd.Tab(),
                 PAINT_GRID | PAINT_LEFT);
+        }
     }
 }
 
diff --git a/sc/source/ui/undo/undoblk.cxx b/sc/source/ui/undo/undoblk.cxx
index 157540535667..ca234549ab83 100644
--- a/sc/source/ui/undo/undoblk.cxx
+++ b/sc/source/ui/undo/undoblk.cxx
@@ -1177,6 +1177,8 @@ void ScUndoDragDrop::PaintArea( ScRange aRange, sal_uInt16 nExtFlags ) const
 
         if (rDoc.SetOptimalHeight(aCxt, aRange.aStart.Row(), aRange.aEnd.Row(), aRange.aStart.Tab()))
         {
+            // tdf#76183: recalculate objects' positions
+            rDoc.SetDrawPageSize(aRange.aStart.Tab());
             aRange.aStart.SetCol(0);
             aRange.aEnd.SetCol(MAXCOL);
             aRange.aEnd.SetRow(MAXROW);
diff --git a/sc/source/ui/undo/undoblk3.cxx b/sc/source/ui/undo/undoblk3.cxx
index ff43c28e0934..5de441787b0e 100644
--- a/sc/source/ui/undo/undoblk3.cxx
+++ b/sc/source/ui/undo/undoblk3.cxx
@@ -884,7 +884,7 @@ void ScUndoAutoFormat::Redo()
                     rDoc.SetRowFlags( nRow, nTab, nOld & ~CR_MANUALSIZE );
             }
 
-            rDoc.SetOptimalHeight(aCxt, nStartY, nEndY, nTab);
+            bool bChanged = rDoc.SetOptimalHeight(aCxt, nStartY, nEndY, nTab);
 
             for (SCCOL nCol=nStartX; nCol<=nEndX; nCol++)
                 if (!rDoc.ColHidden(nCol, nTab))
@@ -895,6 +895,10 @@ void ScUndoAutoFormat::Redo()
                     rDoc.SetColWidth( nCol, nTab, nThisSize );
                     rDoc.ShowCol( nCol, nTab, true );
                 }
+
+            // tdf#76183: recalculate objects' positions
+            if (bChanged)
+                rDoc.SetDrawPageSize(nTab);
         }
 
         pDocShell->PostPaint( 0,      0,      nStartZ,
diff --git a/sc/source/ui/view/viewfun2.cxx b/sc/source/ui/view/viewfun2.cxx
index 7e2e8b316708..6b2b17ae7f61 100644
--- a/sc/source/ui/view/viewfun2.cxx
+++ b/sc/source/ui/view/viewfun2.cxx
@@ -147,6 +147,9 @@ bool ScViewFunc::AdjustBlockHeight( bool bPaint, ScMarkData* pMarkData )
                 bAnyChanged = bChanged = true;
             }
         }
+        // tdf#76183: recalculate objects' positions
+        if (bChanged)
+            rDoc.SetDrawPageSize(nTab);
         if ( bPaint && bChanged )
             pDocSh->PostPaint( 0, nPaintY, nTab, MAXCOL, MAXROW, nTab,
                                                 PAINT_GRID | PAINT_LEFT );
@@ -181,6 +184,10 @@ bool ScViewFunc::AdjustRowHeight( SCROW nStartRow, SCROW nEndRow )
     sc::RowHeightContext aCxt(nPPTX, nPPTY, aZoomX, aZoomY, aProv.GetDevice());
     bool bChanged = rDoc.SetOptimalHeight(aCxt, nStartRow, nEndRow, nTab);
 
+    // tdf#76183: recalculate objects' positions
+    if (bChanged)
+        rDoc.SetDrawPageSize(nTab);
+
     if (bChanged && ( nStartRow == nEndRow ))
     {
         sal_uInt16 nNewPixel = (sal_uInt16) (rDoc.GetRowHeight(nStartRow,nTab) * nPPTY);
commit 07ea76f4997b71c514b599e7acd220f073c74ddd
Author: Tor Lillqvist <tml at collabora.com>
Date:   Sun Nov 26 23:28:05 2017 +0200

    Deduplicate conditional formats loaded from .ods
    
    If there are several separate conditional format elements that can be
    represented as just one (with several ranges), try to do that.
    
    A particular customer document used to take 3 minutes 20 seconds to
    load, and it contained so many (tens of thousands) conditional formats
    that the Format> Conditional Formatting> Manage... dialog was
    practically impossible to use.
    
    Now loading that document takes 15 seconds and there are just a
    handful of separate conditional formats.
    
    Also add a simple unit test to verify the deduplication.
    
    Change-Id: I7c468af99956d4646ee5507390f1476caff52325
    Reviewed-on: https://gerrit.libreoffice.org/45479
    Reviewed-by: Thorsten Behrens <Thorsten.Behrens at CIB.de>
    Tested-by: Thorsten Behrens <Thorsten.Behrens at CIB.de>

diff --git a/sc/CppunitTest_sc_cond_format_merge.mk b/sc/CppunitTest_sc_cond_format_merge.mk
new file mode 100644
index 000000000000..bfb7dc2bba3f
--- /dev/null
+++ b/sc/CppunitTest_sc_cond_format_merge.mk
@@ -0,0 +1,116 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_CppunitTest_CppunitTest,sc_cond_format_merge))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,sc_cond_format_merge, \
+    sc/qa/unit/cond_format_merge \
+))
+
+$(eval $(call gb_CppunitTest_use_externals,sc_cond_format_merge, \
+    boost_headers \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,sc_cond_format_merge, \
+    basegfx \
+    comphelper \
+    cppu \
+    cppuhelper \
+    drawinglayer \
+    editeng \
+    for \
+    forui \
+    i18nlangtag \
+    msfilter \
+    oox \
+    sal \
+    salhelper \
+    sax \
+    sb \
+    sc \
+    scqahelper \
+    sfx \
+    sot \
+    subsequenttest \
+    svl \
+    svt \
+    svx \
+    svxcore \
+    test \
+    tk \
+    tl \
+    ucbhelper \
+    unotest \
+    utl \
+    vbahelper \
+    vcl \
+    xo \
+))
+
+$(eval $(call gb_CppunitTest_set_include,sc_cond_format_merge,\
+    -I$(SRCDIR)/sc/source/ui/inc \
+    -I$(SRCDIR)/sc/inc \
+    $$(INCLUDE) \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,sc_cond_format_merge))
+
+$(eval $(call gb_CppunitTest_use_ure,sc_cond_format_merge))
+$(eval $(call gb_CppunitTest_use_vcl,sc_cond_format_merge))
+
+$(eval $(call gb_CppunitTest_use_components,sc_cond_format_merge,\
+    basic/util/sb \
+    chart2/source/chartcore \
+    chart2/source/controller/chartcontroller \
+    comphelper/util/comphelp \
+    configmgr/source/configmgr \
+    dbaccess/util/dba \
+    embeddedobj/util/embobj \
+    eventattacher/source/evtatt \
+    filter/source/config/cache/filterconfig1 \
+    filter/source/storagefilterdetect/storagefd \
+    forms/util/frm \
+    framework/util/fwk \
+    i18npool/util/i18npool \
+    oox/util/oox \
+    package/source/xstor/xstor \
+    package/util/package2 \
+    sax/source/expatwrap/expwrap \
+    scaddins/source/analysis/analysis \
+    scaddins/source/datefunc/date \
+    scripting/source/basprov/basprov \
+    scripting/util/scriptframe \
+    sc/util/sc \
+    sc/util/scd \
+    sc/util/scfilt \
+    $(call gb_Helper_optional,SCRIPTING, \
+        sc/util/vbaobj) \
+    sfx2/util/sfx \
+    sot/util/sot \
+    svl/source/fsstor/fsstorage \
+    svl/util/svl \
+    svtools/util/svt \
+    svx/util/svx \
+    svx/util/svxcore \
+    toolkit/util/tk \
+    ucb/source/core/ucb1 \
+    ucb/source/ucp/file/ucpfile1 \
+    ucb/source/ucp/tdoc/ucptdoc1 \
+    unotools/util/utl \
+    unoxml/source/rdf/unordf \
+    unoxml/source/service/unoxml \
+	uui/util/uui \
+    xmloff/util/xo \
+))
+
+$(eval $(call gb_CppunitTest_use_configuration,sc_cond_format_merge))
+
+$(eval $(call gb_CppunitTest_use_unittest_configuration,sc_cond_format_merge))
+
+# vim: set noet sw=4 ts=4:
diff --git a/sc/Module_sc.mk b/sc/Module_sc.mk
index 7175f01a2658..26f967d7b267 100644
--- a/sc/Module_sc.mk
+++ b/sc/Module_sc.mk
@@ -52,6 +52,7 @@ $(eval $(call gb_Module_add_check_targets,sc,\
 ))
 
 $(eval $(call gb_Module_add_slowcheck_targets,sc, \
+	CppunitTest_sc_cond_format_merge \
 	CppunitTest_sc_condformats \
 	CppunitTest_sc_new_cond_format_api \
 	CppunitTest_sc_subsequent_filters_test \
diff --git a/sc/inc/conditio.hxx b/sc/inc/conditio.hxx
index 104fe4293ebf..2f6b266f7468 100644
--- a/sc/inc/conditio.hxx
+++ b/sc/inc/conditio.hxx
@@ -233,6 +233,8 @@ public:
 
     bool            operator== ( const ScConditionEntry& r ) const;
 
+    bool            EqualIgnoringSrcPos( const ScConditionEntry& r ) const;
+
     virtual void SetParent( ScConditionalFormat* pNew ) override;
 
     bool IsCellValid( ScRefCellValue& rCell, const ScAddress& rPos ) const;
@@ -241,6 +243,7 @@ public:
     void SetOperation(ScConditionMode eMode);
     bool            IsIgnoreBlank() const       { return ( nOptions & SC_COND_NOBLANKS ) == 0; }
     void            SetIgnoreBlank(bool bSet);
+    OUString        GetSrcString() const         { return aSrcString; }
     const ScAddress& GetSrcPos() const           { return aSrcPos; }
 
     ScAddress       GetValidSrcPos() const;     // adjusted to allow textual representation of expressions
diff --git a/sc/qa/extras/testdocuments/cond_format_merge.ods b/sc/qa/extras/testdocuments/cond_format_merge.ods
new file mode 100644
index 000000000000..43b676d22080
Binary files /dev/null and b/sc/qa/extras/testdocuments/cond_format_merge.ods differ
diff --git a/sc/qa/unit/cond_format_merge.cxx b/sc/qa/unit/cond_format_merge.cxx
new file mode 100644
index 000000000000..0ce3f21909bd
--- /dev/null
+++ b/sc/qa/unit/cond_format_merge.cxx
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/sheet/XConditionalFormats.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <test/bootstrapfixture.hxx>
+#include <test/calc_unoapi_test.hxx>
+
+#include <global.hxx>
+#include <document.hxx>
+
+#include "helper/qahelper.hxx"
+
+using namespace css;
+
+class ScCondFormatMergeTest : public CalcUnoApiTest
+{
+public:
+    ScCondFormatMergeTest();
+
+    void testCondFormatMerge();
+
+    CPPUNIT_TEST_SUITE(ScCondFormatMergeTest);
+    CPPUNIT_TEST(testCondFormatMerge);
+    CPPUNIT_TEST_SUITE_END();
+};
+
+ScCondFormatMergeTest::ScCondFormatMergeTest()
+    : CalcUnoApiTest("sc/qa/extras/testdocuments/")
+{
+}
+
+void ScCondFormatMergeTest::testCondFormatMerge()
+{
+    OUString aFileURL;
+    createFileURL("cond_format_merge.ods", aFileURL);
+    uno::Reference<lang::XComponent> mxComponent = loadFromDesktop(aFileURL);
+
+    CPPUNIT_ASSERT_MESSAGE("Component not loaded", mxComponent.is());
+
+    // get the first sheet
+    uno::Reference<sheet::XSpreadsheetDocument> xDoc(mxComponent, uno::UNO_QUERY_THROW);
+    uno::Reference<container::XIndexAccess> xIndex(xDoc->getSheets(), uno::UNO_QUERY_THROW);
+    uno::Reference<sheet::XSpreadsheet> xSheet(xIndex->getByIndex(0), uno::UNO_QUERY_THROW);
+
+    uno::Reference<beans::XPropertySet> xProps(xSheet, uno::UNO_QUERY_THROW);
+    uno::Any aAny = xProps->getPropertyValue("ConditionalFormats");
+    uno::Reference<sheet::XConditionalFormats> xCondFormats;
+
+    CPPUNIT_ASSERT(aAny >>= xCondFormats);
+    CPPUNIT_ASSERT(xCondFormats.is());
+
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(5), xCondFormats->getLength());
+
+    uno::Sequence<uno::Reference<sheet::XConditionalFormat>> xCondFormatSeq
+        = xCondFormats->getConditionalFormats();
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(5), xCondFormatSeq.getLength());
+
+    int nRanges = 0;
+    for (sal_Int32 i = 0, n = xCondFormatSeq.getLength(); i < n; ++i)
+    {
+        CPPUNIT_ASSERT(xCondFormatSeq[i].is());
+
+        uno::Reference<sheet::XConditionalFormat> xCondFormat = xCondFormatSeq[i];
+        CPPUNIT_ASSERT(xCondFormat.is());
+
+        uno::Reference<beans::XPropertySet> xPropSet(xCondFormat, uno::UNO_QUERY_THROW);
+
+        aAny = xPropSet->getPropertyValue("Range");
+        uno::Reference<sheet::XSheetCellRanges> xCellRanges;
+        CPPUNIT_ASSERT(aAny >>= xCellRanges);
+        CPPUNIT_ASSERT(xCellRanges.is());
+
+        uno::Sequence<table::CellRangeAddress> aRanges = xCellRanges->getRangeAddresses();
+        CPPUNIT_ASSERT_GREATEREQUAL(sal_Int32(1), aRanges.getLength());
+
+        table::CellRangeAddress aRange0 = aRanges[0];
+        CPPUNIT_ASSERT_EQUAL(sal_Int16(0), aRange0.Sheet);
+        CPPUNIT_ASSERT_EQUAL(aRange0.StartColumn, aRange0.EndColumn);
+
+        table::CellRangeAddress aRange1;
+
+        switch (aRange0.StartColumn)
+        {
+            case 3:
+                switch (aRange0.StartRow)
+                {
+                    case 0: // D1:D2,D5::D8
+                        nRanges++;
+                        CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aRange0.EndRow);
+                        CPPUNIT_ASSERT_EQUAL(sal_Int32(2), aRanges.getLength());
+                        aRange1 = aRanges[1];
+                        CPPUNIT_ASSERT_EQUAL(sal_Int16(0), aRange1.Sheet);
+                        CPPUNIT_ASSERT_EQUAL(aRange1.StartColumn, aRange1.EndColumn);
+                        CPPUNIT_ASSERT_EQUAL(sal_Int32(3), aRange1.StartColumn);
+                        CPPUNIT_ASSERT_EQUAL(sal_Int32(4), aRange1.StartRow);
+                        CPPUNIT_ASSERT_EQUAL(sal_Int32(7), aRange1.EndRow);
+                        break;
+                    default:
+                        CPPUNIT_FAIL("Unexpected range in column D");
+                }
+                break;
+            case 5:
+                switch (aRange0.StartRow)
+                {
+                    case 0: // F1:F2
+                        nRanges++;
+                        CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aRange0.EndRow);
+                        CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aRanges.getLength());
+                        break;
+                    case 2: // F3
+                        nRanges++;
+                        CPPUNIT_ASSERT_EQUAL(sal_Int32(2), aRange0.EndRow);
+                        CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aRanges.getLength());
+                        break;
+                    case 3: // F4
+                        nRanges++;
+                        CPPUNIT_ASSERT_EQUAL(sal_Int32(3), aRange0.EndRow);
+                        CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aRanges.getLength());
+                        break;
+                    case 4: // F5
+                        nRanges++;
+                        CPPUNIT_ASSERT_EQUAL(sal_Int32(4), aRange0.EndRow);
+                        CPPUNIT_ASSERT_EQUAL(sal_Int32(1), aRanges.getLength());
+                        break;
+                    default:
+                        CPPUNIT_FAIL("Unexpected range in column F");
+                }
+                break;
+            default:
+                CPPUNIT_FAIL("Unexpected range");
+        }
+    }
+
+    CPPUNIT_ASSERT_EQUAL(5, nRanges);
+
+    closeDocument(mxComponent);
+    mxComponent.clear();
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ScCondFormatMergeTest);
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/conditio.cxx b/sc/source/core/data/conditio.cxx
index 68fa98beb206..d3d996d93239 100644
--- a/sc/source/core/data/conditio.cxx
+++ b/sc/source/core/data/conditio.cxx
@@ -694,6 +694,25 @@ bool ScConditionEntry::operator== ( const ScConditionEntry& r ) const
     return bEq;
 }
 
+bool ScConditionEntry::EqualIgnoringSrcPos( const ScConditionEntry& r ) const
+{
+    bool bEq = (eOp == r.eOp && nOptions == r.nOptions &&
+                lcl_IsEqual( pFormula1, r.pFormula1 ) &&
+                lcl_IsEqual( pFormula2, r.pFormula2 ));
+    if (bEq)
+    {
+        // Here, ignore the aSrcPoses and aSrcStrings
+
+        // If not formulas, compare values
+        if ( !pFormula1 && ( nVal1 != r.nVal1 || aStrVal1 != r.aStrVal1 || bIsStr1 != r.bIsStr1 ) )
+            bEq = false;
+        if ( !pFormula2 && ( nVal2 != r.nVal2 || aStrVal2 != r.aStrVal2 || bIsStr2 != r.bIsStr2 ) )
+            bEq = false;
+    }
+
+    return bEq;
+}
+
 void ScConditionEntry::Interpret( const ScAddress& rPos )
 {
     // Create formula cells
diff --git a/sc/source/filter/xml/xmlcondformat.cxx b/sc/source/filter/xml/xmlcondformat.cxx
index 7e0e0c063ce1..f9704a1afb09 100644
--- a/sc/source/filter/xml/xmlcondformat.cxx
+++ b/sc/source/filter/xml/xmlcondformat.cxx
@@ -19,6 +19,7 @@
 #include "docfunc.hxx"
 #include "XMLConverter.hxx"
 #include "stylehelper.hxx"
+#include "tokenarray.hxx"
 
 ScXMLConditionalFormatsContext::ScXMLConditionalFormatsContext( ScXMLImport& rImport, sal_uInt16 nPrfx,
                         const OUString& rLName):
@@ -38,7 +39,7 @@ SvXMLImportContext* ScXMLConditionalFormatsContext::CreateChildContext( sal_uInt
     switch (nToken)
     {
         case XML_TOK_CONDFORMATS_CONDFORMAT:
-            pContext = new ScXMLConditionalFormatContext( GetScImport(), nPrefix, rLocalName, xAttrList );
+            pContext = new ScXMLConditionalFormatContext( GetScImport(), nPrefix, rLocalName, xAttrList, *this );
             break;
     }
 
@@ -54,11 +55,18 @@ void ScXMLConditionalFormatsContext::EndElement()
     bool bDeleted = !pCondFormatList->CheckAllEntries();
 
     SAL_WARN_IF(bDeleted, "sc", "conditional formats have been deleted because they contained empty range info");
+
+    for (const auto& i : mvCondFormatData)
+    {
+        pDoc->AddCondFormatData( i.mpFormat->GetRange(), i.mnTab, i.mpFormat->GetKey() );
+    }
 }
 
 ScXMLConditionalFormatContext::ScXMLConditionalFormatContext( ScXMLImport& rImport, sal_uInt16 nPrfx,
-                        const OUString& rLName, const css::uno::Reference< css::xml::sax::XAttributeList>& xAttrList):
-    SvXMLImportContext( rImport, nPrfx, rLName )
+                        const OUString& rLName, const css::uno::Reference< css::xml::sax::XAttributeList>& xAttrList,
+                        ScXMLConditionalFormatsContext& rParent ):
+    SvXMLImportContext( rImport, nPrfx, rLName ),
+    mrParent( rParent )
 {
     OUString sRange;
 
@@ -120,16 +128,225 @@ SvXMLImportContext* ScXMLConditionalFormatContext::CreateChildContext( sal_uInt1
     return pContext;
 }
 
+static bool HasRelRefIgnoringSheet0Relative( ScDocument* pDoc, ScTokenArray* pTokens, sal_uInt16 nRecursion = 0 )
+{
+    if (pTokens)
+    {
+        formula::FormulaToken* t;
+        for( t = pTokens->First(); t; t = pTokens->Next() )
+        {
+            switch( t->GetType() )
+            {
+                case formula::svDoubleRef:
+                {
+                    ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2;
+                    if ( rRef2.IsColRel() || rRef2.IsRowRel() || (rRef2.IsFlag3D() && rRef2.IsTabRel()) )
+                        return true;
+                    SAL_FALLTHROUGH;
+                }
+
+                case formula::svSingleRef:
+                {
+                    ScSingleRefData& rRef1 = *t->GetSingleRef();
+                    if ( rRef1.IsColRel() || rRef1.IsRowRel() || (rRef1.IsFlag3D() && rRef1.IsTabRel()) )
+                        return true;
+                }
+                break;
+
+                case formula::svIndex:
+                {
+                    if( t->GetOpCode() == ocName )      // DB areas always absolute
+                        if( ScRangeData* pRangeData = pDoc->FindRangeNameBySheetAndIndex( t->GetSheet(), t->GetIndex()) )
+                            if( (nRecursion < 42) && HasRelRefIgnoringSheet0Relative( pDoc, pRangeData->GetCode(), nRecursion + 1 ) )
+                                return true;
+                }
+                break;
+
+                // #i34474# function result dependent on cell position
+                case formula::svByte:
+                {
+                    switch( t->GetOpCode() )
+                    {
+                        case ocRow:     // ROW() returns own row index
+                        case ocColumn:  // COLUMN() returns own column index
+                        case ocSheet:   // SHEET() returns own sheet index
+                        case ocCell:    // CELL() may return own cell address
+                            return true;
+                        default:
+                            break;
+                    }
+                }
+                break;
+
+                default:
+                    break;
+            }
+        }
+    }
+    return false;
+}
+
+static bool HasOneSingleFullyRelativeReference( ScTokenArray* pTokens, ScSingleRefData& rOffset )
+{
+    int nCount = 0;
+    if (pTokens)
+    {
+        formula::FormulaToken* t;
+        for( t = pTokens->First(); t; t = pTokens->Next() )
+        {
+            switch( t->GetType() )
+            {
+                case formula::svSingleRef:
+                {
+                    ScSingleRefData& rRef1 = *t->GetSingleRef();
+                    if ( rRef1.IsColRel() && rRef1.IsRowRel() && !rRef1.IsFlag3D() && rRef1.IsTabRel() )
+                    {
+                        nCount++;
+                        if (nCount == 1)
+                        {
+                            rOffset = rRef1;
+                        }
+                    }
+                }
+                break;
+
+                default:
+                    break;
+            }
+        }
+    }
+    return nCount == 1;
+}
+
 void ScXMLConditionalFormatContext::EndElement()
 {
     ScDocument* pDoc = GetScImport().GetDocument();
 
     SCTAB nTab = GetScImport().GetTables().GetCurrentSheet();
     ScConditionalFormat* pFormat = mxFormat.release();
+
+    bool bEligibleForCache = true;
+    bool bSingleRelativeReference = false;
+    ScSingleRefData aOffsetForSingleRelRef;
+    ScTokenArray* pTokens = nullptr;
+    for (size_t nFormatEntryIx = 0; nFormatEntryIx < pFormat->size(); ++nFormatEntryIx)
+    {
+        auto pFormatEntry = pFormat->GetEntry(nFormatEntryIx);
+        auto pCondFormatEntry = static_cast<const ScCondFormatEntry*>(pFormatEntry);
+
+        if (pCondFormatEntry->GetOperation() != SC_COND_EQUAL &&
+            pCondFormatEntry->GetOperation() != SC_COND_DIRECT)
+        {
+            bEligibleForCache = false;
+            break;
+        }
+
+        ScAddress aSrcPos;
+        OUString aSrcString = pCondFormatEntry->GetSrcString();
+        if ( !aSrcString.isEmpty() )
+            aSrcPos.Parse( aSrcString, pDoc );
+        ScCompiler aComp( pDoc, aSrcPos );
+        aComp.SetGrammar( formula::FormulaGrammar::GRAM_ODFF );
+        pTokens = aComp.CompileString( pCondFormatEntry->GetExpression(aSrcPos, 0), "" );
+        if (HasRelRefIgnoringSheet0Relative( pDoc, pTokens ))
+        {
+            // In general not eligible, but some might be. We handle one very special case: When the
+            // conditional format has one entry, the reference position is the first cell of the
+            // range, and with a single fully relative reference in its expression. (Possibly these
+            // conditions could be loosened, but I am too tired to think on that right now.)
+            if (pFormat->size() == 1 &&
+                pFormat->GetRange().size() == 1 &&
+                pFormat->GetRange()[0]->aStart == aSrcPos &&
+                HasOneSingleFullyRelativeReference( pTokens, aOffsetForSingleRelRef ))
+            {
+                bSingleRelativeReference = true;
+            }
+            else
+            {
+                bEligibleForCache = false;
+                break;
+            }
+        }
+    }
+
+    if (bEligibleForCache)
+    {
+        for (auto& aCacheEntry : mrParent.maCache)
+            if (aCacheEntry.mnAge < SAL_MAX_INT64)
+                aCacheEntry.mnAge++;
+
+        for (auto& aCacheEntry : mrParent.maCache)
+        {
+            if (!aCacheEntry.mpFormat)
+                continue;
+
+            if (aCacheEntry.mpFormat->size() != pFormat->size())
+                continue;
+
+            // Check if the conditional format is identical to an existing one (but with different range) and can be shared
+            for (size_t nFormatEntryIx = 0; nFormatEntryIx < pFormat->size(); ++nFormatEntryIx)
+            {
+                auto pCacheFormatEntry = aCacheEntry.mpFormat->GetEntry(nFormatEntryIx);
+                auto pFormatEntry = pFormat->GetEntry(nFormatEntryIx);
+                if (pCacheFormatEntry->GetType() != pFormatEntry->GetType() ||
+                    pFormatEntry->GetType() != condformat::CONDITION)
+                    break;
+
+                auto pCacheCondFormatEntry = static_cast<const ScCondFormatEntry*>(pCacheFormatEntry);
+                auto pCondFormatEntry = static_cast<const ScCondFormatEntry*>(pFormatEntry);
+
+                if (pCacheCondFormatEntry->GetStyle() != pCondFormatEntry->GetStyle())
+                    break;
+
+                // Note That comparing the formulas of the ScConditionEntry at this stage is
+                // comparing just the *strings* of the formulas. For the bSingleRelativeReference
+                // case we compare the tokenized ("compiled") formulas.
+                if (bSingleRelativeReference)
+                {
+                    if (aCacheEntry.mbSingleRelativeReference &&
+                        pTokens->EqualTokens(aCacheEntry.mpTokens.get()))
+                        ;
+                    else
+                        break;
+                }
+                else if (!pCacheCondFormatEntry->EqualIgnoringSrcPos(*pCondFormatEntry))
+                {
+                    break;
+                }
+                // If we get here on the last round through the for loop, we have a cache hit
+                if (nFormatEntryIx == pFormat->size() - 1)
+                {
+                    // Mark cache entry as fresh, do necessary mangling of it and just return
+                    aCacheEntry.mnAge = 0;
+                    for (size_t k = 0; k < pFormat->GetRange().size(); ++k)
+                        aCacheEntry.mpFormat->GetRangeList().Join(*(pFormat->GetRange()[k]));
+                    return;
+                }
+            }
+        }
+
+        // Not found in cache, replace oldest cache entry
+        sal_Int64 nOldestAge = -1;
+        size_t nIndexOfOldest = 0;
+        for (auto& aCacheEntry : mrParent.maCache)
+        {
+            if (aCacheEntry.mnAge > nOldestAge)
+            {
+                nOldestAge = aCacheEntry.mnAge;
+                nIndexOfOldest = (&aCacheEntry - &mrParent.maCache.front());
+            }
+        }
+        mrParent.maCache[nIndexOfOldest].mpFormat = pFormat;
+        mrParent.maCache[nIndexOfOldest].mbSingleRelativeReference = bSingleRelativeReference;
+        mrParent.maCache[nIndexOfOldest].mpTokens.reset(pTokens);
+        mrParent.maCache[nIndexOfOldest].mnAge = 0;
+    }
+
     sal_uLong nIndex = pDoc->AddCondFormat(pFormat, nTab);
-    pFormat->SetKey(nIndex);
+    (void) nIndex; // Avoid 'unused variable' warning when assert() expands to empty
+    assert(pFormat->GetKey() == nIndex);
 
-    pDoc->AddCondFormatData( pFormat->GetRange(), nTab, nIndex);
+    mrParent.mvCondFormatData.push_back( { pFormat, nTab } );
 }
 
 ScXMLConditionalFormatContext::~ScXMLConditionalFormatContext()
diff --git a/sc/source/filter/xml/xmlcondformat.hxx b/sc/source/filter/xml/xmlcondformat.hxx
index ca42c0b6eed5..7a94d6d7fe4a 100644
--- a/sc/source/filter/xml/xmlcondformat.hxx
+++ b/sc/source/filter/xml/xmlcondformat.hxx
@@ -10,9 +10,11 @@
 #ifndef INCLUDED_SC_SOURCE_FILTER_XML_XMLCONDFORMAT_HXX
 #define INCLUDED_SC_SOURCE_FILTER_XML_XMLCONDFORMAT_HXX
 
+#include <array>
 #include <xmloff/xmlictxt.hxx>
 #include "xmlimprt.hxx"
 #include "rangelst.hxx"
+#include "tokenarray.hxx"
 
 class ScColorScaleFormat;
 class ScColorScaleEntry;
@@ -23,6 +25,21 @@ struct ScIconSetFormatData;
 
 class ScXMLConditionalFormatsContext : public SvXMLImportContext
 {
+private:
+    struct CacheEntry
+    {
+        ScConditionalFormat* mpFormat = nullptr;
+        bool mbSingleRelativeReference;
+        std::unique_ptr<const ScTokenArray> mpTokens;
+        sal_Int64 mnAge = SAL_MAX_INT64;
+    };
+
+    struct CondFormatData
+    {
+        ScConditionalFormat* mpFormat;
+        SCTAB mnTab;
+    };
+
     const ScXMLImport& GetScImport() const { return static_cast<const ScXMLImport&>(GetImport()); }
     ScXMLImport& GetScImport() { return static_cast<ScXMLImport&>(GetImport()); }
 public:
@@ -36,6 +53,10 @@ public:
                                      const css::uno::Reference<css::xml::sax::XAttributeList>& xAttrList ) override;
 
     virtual void EndElement() override;
+
+    std::array<CacheEntry, 4> maCache;
+
+    std::vector<CondFormatData> mvCondFormatData;
 };
 
 class ScXMLConditionalFormatContext : public SvXMLImportContext
@@ -45,7 +66,8 @@ class ScXMLConditionalFormatContext : public SvXMLImportContext
 public:
     ScXMLConditionalFormatContext( ScXMLImport& rImport, sal_uInt16 nPrfx,
                         const OUString& rLName,
-                        const css::uno::Reference<css::xml::sax::XAttributeList>& xAttrList);
+                        const css::uno::Reference<css::xml::sax::XAttributeList>& xAttrList,
+                        ScXMLConditionalFormatsContext& rParent );
 
     virtual ~ScXMLConditionalFormatContext();
 
@@ -58,6 +80,8 @@ private:
 
     std::unique_ptr<ScConditionalFormat> mxFormat;
     ScRangeList maRange;
+
+    ScXMLConditionalFormatsContext& mrParent;
 };
 
 class ScXMLColorScaleFormatContext : public SvXMLImportContext
commit 2aaa0c7dfbc431a0d9cd8946f0e016003da08bd1
Author: Tor Lillqvist <tml at collabora.com>
Date:   Tue Nov 28 12:38:03 2017 +0200

    Do as the FIXME suggested
    
    Not exactly, though. The FIXME said "Make this a comparison operator
    at the TokenArray?" but I think that would be misleading as the code
    in question specifically does not check the TokenArrays for being
    completely identical; it intentionally ignores the RPN part. So make
    it a member function 'EqualTokens' instead.
    
    Change-Id: I15d840c422844fa144415a76c1f8fcbd6cae3c83
    Reviewed-on: https://gerrit.libreoffice.org/45462
    Reviewed-by: Thorsten Behrens <Thorsten.Behrens at CIB.de>
    Tested-by: Thorsten Behrens <Thorsten.Behrens at CIB.de>

diff --git a/sc/inc/tokenarray.hxx b/sc/inc/tokenarray.hxx
index b2b4edd896b3..e37d8e97cf1d 100644
--- a/sc/inc/tokenarray.hxx
+++ b/sc/inc/tokenarray.hxx
@@ -58,6 +58,9 @@ public:
     /// Assignment with references to FormulaToken entries (not copied!)
     ScTokenArray( const ScTokenArray& );
     virtual ~ScTokenArray();
+
+    bool EqualTokens( const ScTokenArray* pArr2 ) const;
+
     void ClearScTokenArray();
     ScTokenArray* Clone() const;    /// True copy!
 
diff --git a/sc/source/core/data/conditio.cxx b/sc/source/core/data/conditio.cxx
index 7fd1ef53ac69..68fa98beb206 100644
--- a/sc/source/core/data/conditio.cxx
+++ b/sc/source/core/data/conditio.cxx
@@ -663,26 +663,11 @@ void ScConditionEntry::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt )
     StartListening();
 }
 
-//FIXME: Make this a comparison operator at the TokenArray?
 static bool lcl_IsEqual( const ScTokenArray* pArr1, const ScTokenArray* pArr2 )
 {
     // We only compare the non-RPN array
     if ( pArr1 && pArr2 )
-    {
-        sal_uInt16 nLen = pArr1->GetLen();
-        if ( pArr2->GetLen() != nLen )
-            return false;
-
-        FormulaToken** ppToken1 = pArr1->GetArray();
-        FormulaToken** ppToken2 = pArr2->GetArray();
-        for (sal_uInt16 i=0; i<nLen; i++)
-        {
-            if ( ppToken1[i] != ppToken2[i] &&
-                 !(*ppToken1[i] == *ppToken2[i]) )
-                return false; // Difference
-        }
-        return true; // All entries are the same
-    }
+        return pArr1->EqualTokens( pArr2 );
     else
         return !pArr1 && !pArr2; // Both 0? -> the same
 }
diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx
index 6671a5ab5dbc..ce2c7cd8b6cd 100644
--- a/sc/source/core/tool/token.cxx
+++ b/sc/source/core/tool/token.cxx
@@ -1770,6 +1770,23 @@ ScTokenArray& ScTokenArray::operator=( const ScTokenArray& rArr )
     return *this;
 }
 
+bool ScTokenArray::EqualTokens( const ScTokenArray* pArr2) const
+{
+    // We only compare the non-RPN array
+    if ( pArr2->nLen != nLen )
+        return false;
+
+    FormulaToken** ppToken1 = GetArray();
+    FormulaToken** ppToken2 = pArr2->GetArray();
+    for (sal_uInt16 i=0; i<nLen; i++)
+    {
+        if ( ppToken1[i] != ppToken2[i] &&
+             !(*ppToken1[i] == *ppToken2[i]) )
+            return false; // Difference
+    }
+    return true; // All entries are the same
+}
+
 void ScTokenArray::ClearScTokenArray()
 {
     Clear();
commit d26c8dab20e8b4b9ff09cada937abe6150c56840
Author: Szymon Kłos <szymon.klos at collabora.com>
Date:   Wed Nov 29 14:10:13 2017 +0100

    tdf#76646 don't open link on Ctrl-click if not required
    
    Change-Id: Ie081f8144e50f576b9f8acb2ddd5b1c891533964
    Reviewed-on: https://gerrit.libreoffice.org/45499
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Szymon Kłos <szymon.klos at collabora.com>
    Reviewed-on: https://gerrit.libreoffice.org/45555
    Reviewed-by: Thorsten Behrens <Thorsten.Behrens at CIB.de>
    Tested-by: Thorsten Behrens <Thorsten.Behrens at CIB.de>

diff --git a/sd/source/ui/func/fusel.cxx b/sd/source/ui/func/fusel.cxx
index 425a4d9c857d..fa5fa58349bc 100644
--- a/sd/source/ui/func/fusel.cxx
+++ b/sd/source/ui/func/fusel.cxx
@@ -274,6 +274,8 @@ bool FuSelection::MouseButtonDown(const MouseEvent& rMEvt)
                 SvtSecurityOptions aSecOpt;
                 if (!rMEvt.IsMod1() && aSecOpt.IsOptionSet(SvtSecurityOptions::E_CTRLCLICK_HYPERLINK))
                     return true;
+                if (rMEvt.IsMod1() && !aSecOpt.IsOptionSet(SvtSecurityOptions::E_CTRLCLICK_HYPERLINK))
+                    return true;
 
                 SfxStringItem aStrItem(SID_FILE_NAME, aVEvt.pURLField->GetURL());
                 SfxStringItem aReferer(SID_REFERER, mpDocSh->GetMedium()->GetName());
commit 51bf4f1bbdd0ea4817b2bbf8e09ecce6d6816b80
Author: Szymon Kłos <szymon.klos at collabora.com>
Date:   Tue Nov 28 20:15:47 2017 +0100

    tdf#76646 Ctrl-click required for hyperlinks in draw/impress
    
    If "Ctrl-click required to follow hyperlinks" is set in
    Options -> LibreOffice -> Security -> Options
    open the link only with Ctrl key pressed.
    
    Change-Id: Icf57b4deedabd51f31f04021ba3f6bddc3829931
    Reviewed-on: https://gerrit.libreoffice.org/45437
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Szymon Kłos <szymon.klos at collabora.com>
    Reviewed-on: https://gerrit.libreoffice.org/45554
    Reviewed-by: Thorsten Behrens <Thorsten.Behrens at CIB.de>
    Tested-by: Thorsten Behrens <Thorsten.Behrens at CIB.de>

diff --git a/sd/source/ui/func/fusel.cxx b/sd/source/ui/func/fusel.cxx
index 2979c9099416..425a4d9c857d 100644
--- a/sd/source/ui/func/fusel.cxx
+++ b/sd/source/ui/func/fusel.cxx
@@ -27,6 +27,7 @@
 #include <svtools/imapobj.hxx>
 #include <svl/urihelper.hxx>
 #include <unotools/localfilehelper.hxx>
+#include <unotools/securityoptions.hxx>
 #include <svx/svxids.hrc>
 #include <svx/xfillit0.hxx>
 #include <sfx2/app.hxx>
@@ -269,6 +270,11 @@ bool FuSelection::MouseButtonDown(const MouseEvent& rMEvt)
                       aVEvt.eEvent == SDREVENT_EXECUTEURL )
              {
                 mpWindow->ReleaseMouse();
+
+                SvtSecurityOptions aSecOpt;
+                if (!rMEvt.IsMod1() && aSecOpt.IsOptionSet(SvtSecurityOptions::E_CTRLCLICK_HYPERLINK))
+                    return true;
+
                 SfxStringItem aStrItem(SID_FILE_NAME, aVEvt.pURLField->GetURL());
                 SfxStringItem aReferer(SID_REFERER, mpDocSh->GetMedium()->GetName());
                 SfxBoolItem aBrowseItem( SID_BROWSE, true );
commit e0dc344395393c8a9364952a5d241c12fa8b8f54
Author: Caolán McNamara <caolanm at redhat.com>
Date:   Thu Nov 2 17:23:00 2017 +0000

    Resolves: tdf#113160 changing all warning dialogs to non-modal is unsafe
    
    existing code doesn't expect that so stuff crashes
    
    partial revert of...
    
    commit db6b703d391838c481fd090065f6d329edcd4efa
    Date:   Thu Aug 24 18:32:38 2017 +0200
    
        Allow non-modal Dialogs during FileImport/Load
    
    Change-Id: I152feb849186cf035664a700d3f94ee049cdf6d3
    Reviewed-on: https://gerrit.libreoffice.org/44227
    Reviewed-by: Caolán McNamara <caolanm at redhat.com>
    Tested-by: Caolán McNamara <caolanm at redhat.com>
    
    Related: tdf#113160 set a temporary dialog parent during type detection
    
    to get warning dialogs that don't block the existing windows but whose
    lifecycle can be controlled to avoid crashes during exit
    
    Change-Id: I57965301c3d8a031acb33e83bf7715fe132385d0
    Reviewed-on: https://gerrit.libreoffice.org/45044
    Reviewed-by: Caolán McNamara <caolanm at redhat.com>
    Tested-by: Caolán McNamara <caolanm at redhat.com>
    Reviewed-on: https://gerrit.libreoffice.org/45400
    Reviewed-by: Thorsten Behrens <Thorsten.Behrens at CIB.de>
    Tested-by: Thorsten Behrens <Thorsten.Behrens at CIB.de>

diff --git a/framework/Library_fwe.mk b/framework/Library_fwe.mk
index 0559236b2bad..d94cb100a975 100644
--- a/framework/Library_fwe.mk
+++ b/framework/Library_fwe.mk
@@ -46,6 +46,7 @@ $(eval $(call gb_Library_use_libraries,fwe,\
     svl \
     svt \
     tl \
+    tk \
     utl \
     vcl \
 	$(gb_UWINAPI) \
diff --git a/framework/source/dispatch/closedispatcher.cxx b/framework/source/dispatch/closedispatcher.cxx
index 9aa25190a544..96df4fe9d9e5 100644
--- a/framework/source/dispatch/closedispatcher.cxx
+++ b/framework/source/dispatch/closedispatcher.cxx
@@ -36,8 +36,6 @@
 #include <vcl/window.hxx>
 #include <vcl/svapp.hxx>
 #include <vcl/syswin.hxx>
-#include <osl/mutex.hxx>
-#include <vcl/dialog.hxx>
 #include <unotools/moduleoptions.hxx>
 #include <comphelper/processfactory.hxx>
 
@@ -363,14 +361,6 @@ IMPL_LINK_NOARG_TYPED(CloseDispatcher, impl_asyncCallback, LinkParamNone*, void)
         }
     }
 
-    // if we still have dialogs open, temporary suppress termination
-    if (bTerminateApp && Dialog::AreDialogsOpen())
-    {
-        Application::SetShutdownDelayed();
-        bCloseFrame = true;
-        bTerminateApp = false;
-    }
-
     // Do it now ...
     bool bSuccess = false;
     if (bCloseFrame)
diff --git a/framework/source/fwe/interaction/preventduplicateinteraction.cxx b/framework/source/fwe/interaction/preventduplicateinteraction.cxx
index 818fdfe2122a..9e43e1b1d44f 100644
--- a/framework/source/fwe/interaction/preventduplicateinteraction.cxx
+++ b/framework/source/fwe/interaction/preventduplicateinteraction.cxx
@@ -19,6 +19,7 @@
 
 #include <framework/preventduplicateinteraction.hxx>
 
+#include <comphelper/processfactory.hxx>
 #include <osl/diagnose.h>
 
 #include <com/sun/star/task/InteractionHandler.hpp>
@@ -53,7 +54,9 @@ void PreventDuplicateInteraction::useDefaultUUIHandler()
     aLock.clear();
     // <- SAFE
 
-    css::uno::Reference< css::task::XInteractionHandler > xHandler( css::task::InteractionHandler::createWithParent( m_xContext, nullptr ), css::uno::UNO_QUERY_THROW );
+    m_xWarningDialogsParent.reset(new WarningDialogsParentScope(m_xContext));
+    css::uno::Reference<css::task::XInteractionHandler> xHandler(css::task::InteractionHandler::createWithParent(
+        m_xContext, m_xWarningDialogsParent->GetDialogParent()), css::uno::UNO_QUERY_THROW);
 
     // SAFE ->
     aLock.reset();
@@ -236,6 +239,11 @@ bool PreventDuplicateInteraction::getInteractionInfo(const css::uno::Type&
     return false;
 }
 
+IMPL_STATIC_LINK_NOARG_TYPED(WarningDialogsParent, TerminateDesktop, void*, void)
+{
+    css::frame::Desktop::create(comphelper::getProcessComponentContext())->terminate();
+}
+
 } // namespace framework
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/loadenv/loadenv.cxx b/framework/source/loadenv/loadenv.cxx
index c789f4d6e957..5e51c42bee83 100644
--- a/framework/source/loadenv/loadenv.cxx
+++ b/framework/source/loadenv/loadenv.cxx
@@ -378,10 +378,6 @@ void LoadEnv::startLoading()
     if (!bStarted)
         bStarted = impl_loadContent();
 
-    // This may have triggered Dialogs (error cases) that may have
-    // delayed the shutdown, so give delayed shutdown a chance
-    Application::TriggerShutdownDelayed();
-
     // not started => general error
     // We can't say - what was the reason for.
     if (!bStarted)
@@ -1077,7 +1073,7 @@ bool LoadEnv::impl_loadContent()
 
     if (!bHidden && !bMinimized && !bPreview && !xProgress.is())
     {
-        // Note: its an optional interface!
+        // Note: it's an optional interface!
         css::uno::Reference< css::task::XStatusIndicatorFactory > xProgressFactory(xTargetFrame, css::uno::UNO_QUERY);
         if (xProgressFactory.is())
         {
diff --git a/include/framework/preventduplicateinteraction.hxx b/include/framework/preventduplicateinteraction.hxx
index cf6ac0058eda..00c76089da2a 100644
--- a/include/framework/preventduplicateinteraction.hxx
+++ b/include/framework/preventduplicateinteraction.hxx
@@ -24,17 +24,127 @@
 
 #include <vector>
 
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/TerminationVetoException.hpp>
+#include <com/sun/star/frame/XTerminateListener2.hpp>
 #include <com/sun/star/task/XInteractionHandler2.hpp>
 #include <com/sun/star/task/XInteractionRequest.hpp>
 
+#include <cppuhelper/compbase.hxx>
 #include <cppuhelper/implbase.hxx>
 
+#include <sfx2/app.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/dialog.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/wrkwin.hxx>
+
 namespace com { namespace sun { namespace star { namespace uno {
     class XComponentContext;
 } } } }
 
 namespace framework{
 
+inline void closedialogs(SystemWindow& rTopLevel, bool bCloseRoot)
+{
+    for (vcl::Window *pChild = rTopLevel.GetWindow(GetWindowType::FirstTopWindowChild); pChild; pChild = rTopLevel.GetWindow(GetWindowType::NextTopWindowSibling))
+        closedialogs(dynamic_cast<SystemWindow&>(*pChild), true);
+    if (bCloseRoot)
+        rTopLevel.Close();
+}
+
+// This is intended to be the parent for any warning dialogs launched
+// during the load of a document so that those dialogs are modal to
+// this window and don't block any existing windows.
+//
+// If there are dialog children open on exit then veto termination,
+// close the topmost dialog and retry termination.
+class WarningDialogsParent :
+    public cppu::WeakComponentImplHelper<css::frame::XTerminateListener>
+{
+private:
+    osl::Mutex m_aLock;
+    VclPtr<WorkWindow> m_xWin;
+    css::uno::Reference<css::awt::XWindow> m_xInterface;
+
+private:
+
+    DECL_STATIC_LINK_TYPED(WarningDialogsParent, TerminateDesktop, void*, void);
+
+    void closewarningdialogs()
+    {
+        if (!m_xWin)
+            return;
+        SolarMutexGuard aSolarGuard;
+        closedialogs(dynamic_cast<SystemWindow&>(*m_xWin), false);
+    }
+
+public:
+
+    using cppu::WeakComponentImplHelperBase::disposing;
+    virtual void SAL_CALL disposing(const css::lang::EventObject&) throw (::css::uno::RuntimeException, ::std::exception) override
+    {
+    }
+
+    // XTerminateListener
+    virtual void SAL_CALL queryTermination(const css::lang::EventObject&) throw (::css::frame::TerminationVetoException, ::css::uno::RuntimeException, ::std::exception) override
+    {
+        closewarningdialogs();
+        Application::PostUserEvent(LINK(this, WarningDialogsParent, TerminateDesktop));
+        throw css::frame::TerminationVetoException();
+    }
+
+    virtual void SAL_CALL notifyTermination(const css::lang::EventObject&) throw (::css::uno::RuntimeException, ::std::exception) override
+    {
+    }
+
+public:
+    WarningDialogsParent()
+        : cppu::WeakComponentImplHelper<css::frame::XTerminateListener>(m_aLock)
+    {
+        SolarMutexGuard aSolarGuard;
+        m_xWin = VclPtr<WorkWindow>::Create(nullptr, WB_STDWORK);
+        m_xWin->SetText("dialog parent for warning dialogs during load");
+        m_xInterface = VCLUnoHelper::GetInterface(m_xWin);
+    }
+
+    virtual ~WarningDialogsParent() override
+    {
+        closewarningdialogs();
+        m_xWin.disposeAndClear();
+    }
+
+    const css::uno::Reference<css::awt::XWindow>& GetDialogParent() const
+    {
+        return m_xInterface;
+    }
+};
+
+class WarningDialogsParentScope
+{
+private:
+    css::uno::Reference<css::frame::XDesktop> m_xDesktop;
+    rtl::Reference<WarningDialogsParent> m_xListener;
+
+public:
+    WarningDialogsParentScope(const css::uno::Reference<css::uno::XComponentContext>& rContext)
+        : m_xDesktop(css::frame::Desktop::create(rContext), css::uno::UNO_QUERY_THROW)
+        , m_xListener(new WarningDialogsParent)
+    {
+        m_xDesktop->addTerminateListener(m_xListener.get());
+    }
+
+    const css::uno::Reference<css::awt::XWindow>& GetDialogParent() const
+    {
+        return m_xListener->GetDialogParent();
+    }
+
+    ~WarningDialogsParentScope()
+    {
+        m_xDesktop->removeTerminateListener(m_xListener.get());
+    }
+};
+
 /**
     @short      Prevent us from showing the same interaction more than once during
                 the same transaction.
@@ -101,6 +211,8 @@ class FWE_DLLPUBLIC PreventDuplicateInteraction : private ThreadHelpBase2
             if it's not blocked. */
         css::uno::Reference< css::task::XInteractionHandler > m_xHandler;
 
+        std::unique_ptr<WarningDialogsParentScope> m_xWarningDialogsParent;
+
         /** This list describe which and how incoming interactions must be handled.
             Further it contains all collected information after this interaction
             object was used.*/
diff --git a/include/vcl/dialog.hxx b/include/vcl/dialog.hxx
index 87588d50c85d..34e03fc03a99 100644
--- a/include/vcl/dialog.hxx
+++ b/include/vcl/dialog.hxx
@@ -38,11 +38,8 @@ public:
         /** Use given parent or get a default one using GetDefaultParent(...) */
         Default,
 
-        /** Suppress Parent so that Parent is not blocked (kind of modal mode) */
-        NoParent,
-
-        /** Suppress Parent (no modal, see above) and additionally center on default parent */
-        NoParentCentered
+        /** No Parent */
+        NoParent
     };
 
 private:
@@ -124,8 +121,7 @@ public:
 
 
     void            EndDialog( long nResult = 0 );
-    static void     EndAllDialogs( vcl::Window* pParent=nullptr );
-    static bool     AreDialogsOpen();
+    static void     EndAllDialogs( vcl::Window const * pParent );
 
     void            GetDrawWindowBorder( sal_Int32& rLeftBorder, sal_Int32& rTopBorder,
                                          sal_Int32& rRightBorder, sal_Int32& rBottomBorder ) const;
diff --git a/include/vcl/msgbox.hxx b/include/vcl/msgbox.hxx
index 0f526f618fe6..f3351664574c 100644
--- a/include/vcl/msgbox.hxx
+++ b/include/vcl/msgbox.hxx
@@ -47,8 +47,7 @@ protected:
 
 public:
                         MessBox( vcl::Window* pParent, WinBits nStyle,
-                                 const OUString& rTitle, const OUString& rMessage,
-                            Dialog::InitFlag eInitFlag = Dialog::InitFlag::NoParentCentered);
+                                 const OUString& rTitle, const OUString& rMessage);
     virtual             ~MessBox();
     virtual void        dispose() override;
 
diff --git a/include/vcl/svapp.hxx b/include/vcl/svapp.hxx
index 2be63c268198..34b659fa7f33 100644
--- a/include/vcl/svapp.hxx
+++ b/include/vcl/svapp.hxx
@@ -1451,12 +1451,6 @@ public:
     // For vclbootstrapprotector:
     static void setDeInitHook(Link<LinkParamNone*,void> const & hook);
 
-    // for delayed shutdown: set using SetShutdownDelayed, then
-    // trigger using TriggerShutdownDelayed which may actually shutdown
-    // when SetShutdownDelayed is set
-    static void SetShutdownDelayed();
-    static void TriggerShutdownDelayed();
-
 private:
     DECL_STATIC_LINK_TYPED( Application, PostEventHandler, void*, void );
 };
diff --git a/uui/source/iahndl.cxx b/uui/source/iahndl.cxx
index b53f5b474323..e94947ecc233 100644
--- a/uui/source/iahndl.cxx
+++ b/uui/source/iahndl.cxx
@@ -998,33 +998,10 @@ executeMessageBox(
     vcl::Window * pParent,
     OUString const & rTitle,
     OUString const & rMessage,
-    WinBits nButtonMask,
-    Dialog::InitFlag eInitFlag)
+    WinBits nStyle)
 {
     SolarMutexGuard aGuard;
-    ScopedVclPtrInstance< MessBox > xBox(pParent, nButtonMask, rTitle, rMessage, eInitFlag);
-
-    if (Dialog::InitFlag::NoParentCentered == eInitFlag)
-    {
-        vcl::Window* pDefaultParent = Dialog::GetDefaultParent(nButtonMask);
-
-        if (pDefaultParent)
-        {
-            // need to 'Show' to have the following tasks do someting, does
-            // not work without and may even stumble on nullptrs/errors
-            xBox->Show();
-
-            // center on parent window
-            const Point aP(pDefaultParent->GetPosPixel());
-            const Size aS(pDefaultParent->GetSizePixel());
-            const Size aMySize(xBox->GetSizePixel());
-
-            xBox->SetPosPixel(
-                Point(
-                    aP.X() + ((aS.Width() - aMySize.Width()) >> 1),
-                    aP.Y() + ((aS.Height() - aMySize.Height()) >> 1)));
-        }
-    }
+    ScopedVclPtrInstance< MessBox > xBox(pParent, nStyle, rTitle, rMessage);
 
     sal_uInt16 aResult = xBox->Execute();
     switch( aResult )
@@ -1175,8 +1152,7 @@ UUIInteractionHelper::handleGenericErrorRequest(
                 aTitle += " - " ;
             aTitle += aErrTitle;
 
-            executeMessageBox(
-                getParentProperty(), aTitle, aErrorString, WB_OK, Dialog::InitFlag::NoParentCentered);
+            executeMessageBox(getParentProperty(), aTitle, aErrorString, WB_OK);
         }
         else
             ErrorHandler::HandleError(nErrorCode);
@@ -1299,8 +1275,7 @@ UUIInteractionHelper::handleBrokenPackageRequest(
         " " +
         utl::ConfigManager::getProductVersion() );
 
-    switch (
-        executeMessageBox( getParentProperty(), title, aMessage, nButtonMask, Dialog::InitFlag::NoParentCentered) )
+    switch (executeMessageBox(getParentProperty(), title, aMessage, nButtonMask))
     {
     case ERRCODE_BUTTON_OK:
         OSL_ENSURE( xAbort.is(), "unexpected situation" );
diff --git a/vcl/source/app/svapp.cxx b/vcl/source/app/svapp.cxx
index fc6fefc7d958..6af3c7c1c7cf 100644
--- a/vcl/source/app/svapp.cxx
+++ b/vcl/source/app/svapp.cxx
@@ -1816,20 +1816,4 @@ void Application::setDeInitHook(Link<LinkParamNone*,void> const & hook) {
     pSVData->maAppData.mbInAppMain = true;
 }
 
-void Application::SetShutdownDelayed()
-{
-    ImplSVData * pSVData = ImplGetSVData();
-    pSVData->maAppData.mbShutdownDelayed = true;
-}
-
-void Application::TriggerShutdownDelayed()
-{
-    ImplSVData * pSVData = ImplGetSVData();
-
-    if (pSVData->maAppData.mbShutdownDelayed && !Dialog::AreDialogsOpen())
-    {
-        Application::PostUserEvent(LINK(nullptr, ImplSVAppData, ImplPrepareExitMsg));
-    }
-}
-
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/dialog.cxx b/vcl/source/window/dialog.cxx
index 95fed10ab0a6..d0aa084e777e 100644
--- a/vcl/source/window/dialog.cxx
+++ b/vcl/source/window/dialog.cxx
@@ -406,7 +406,7 @@ void Dialog::ImplInit( vcl::Window* pParent, WinBits nStyle, InitFlag eFlag )
     // Now, all Dialogs are per default system windows !!!
     nStyle |= WB_SYSTEMWINDOW;
 
-    if (InitFlag::NoParent == eFlag || InitFlag::NoParentCentered == eFlag)
+    if (InitFlag::NoParent == eFlag)
     {
         pParent = nullptr;
     }
@@ -992,7 +992,7 @@ long Dialog::GetResult() const
     return mpDialogImpl->mnResult;
 }
 
-void Dialog::EndAllDialogs( vcl::Window* pParent )
+void Dialog::EndAllDialogs( vcl::Window const * pParent )
 {
     ImplSVData* pSVData = ImplGetSVData();
     Dialog* pTempModDialog;
@@ -1009,14 +1009,6 @@ void Dialog::EndAllDialogs( vcl::Window* pParent )
     }
 }
 
-bool Dialog::AreDialogsOpen()
-{
-    ImplSVData* pSVData = ImplGetSVData();
-    Dialog* pModDialog = pSVData->maWinData.mpLastExecuteDlg;
-
-    return (nullptr != pModDialog);
-}
-
 void Dialog::SetModalInputMode( bool bModal )
 {
     if ( bModal == mbModalMode )
diff --git a/vcl/source/window/msgbox.cxx b/vcl/source/window/msgbox.cxx
index f6fd26773624..172c9870889c 100644
--- a/vcl/source/window/msgbox.cxx
+++ b/vcl/source/window/msgbox.cxx
@@ -138,12 +138,14 @@ void MessBox::ImplInitButtons()
 }
 
 MessBox::MessBox( vcl::Window* pParent, WinBits nStyle,
-                  const OUString& rTitle, const OUString& rMessage, Dialog::InitFlag eInitFlag) :
+                  const OUString& rTitle, const OUString& rMessage) :
     ButtonDialog( WINDOW_MESSBOX ),
-    maMessText( rMessage )
+    maMessText( rMessage ),
+    mbHelpBtn( false ),
+    mbCheck( false )
 {
     ImplInitMessBoxData();
-    ImplInit( pParent, nStyle | WB_MOVEABLE | WB_HORZ | WB_CENTER, eInitFlag);
+    ImplInit( pParent, nStyle | WB_MOVEABLE | WB_HORZ | WB_CENTER);
     ImplInitButtons();
 
     if ( !rTitle.isEmpty() )


More information about the Libreoffice-commits mailing list