chart2/qa/extras/chart2export3.cxx                          |   70 +++++++-
 chart2/qa/extras/data/xlsx/tdf39052.xlsx                    |binary
 chart2/source/controller/chartapiwrapper/TitleWrapper.cxx   |   65 +++++++-
 chart2/source/controller/dialogs/TitleDialogData.cxx        |    2 
 chart2/source/controller/inc/ChartController.hxx            |    5 
 chart2/source/controller/inc/SelectionHelper.hxx            |    1 
 chart2/source/controller/main/ChartController.cxx           |    6 
 chart2/source/controller/main/ChartController_TextEdit.cxx  |   83 ++++++++--
 chart2/source/controller/main/ChartController_Tools.cxx     |   11 +
 chart2/source/controller/main/ChartController_Window.cxx    |   10 -
 chart2/source/controller/main/ControllerCommandDispatch.cxx |    6 
 chart2/source/controller/main/SelectionHelper.cxx           |    5 
 chart2/source/controller/sidebar/ChartElementsPanel.cxx     |    3 
 chart2/source/inc/TitleHelper.hxx                           |    6 
 chart2/source/tools/TitleHelper.cxx                         |   97 ++++++++----
 chart2/source/view/main/PropertyMapper.cxx                  |    3 
 chart2/source/view/main/ShapeFactory.cxx                    |   74 ++++++---
 chart2/uiconfig/menubar/menubar.xml                         |    1 
 chart2/uiconfig/toolbar/toolbar.xml                         |    1 
 editeng/source/editeng/editobj.cxx                          |   14 +
 editeng/source/editeng/editobj2.hxx                         |    2 
 editeng/source/outliner/overflowingtxt.cxx                  |    2 
 include/editeng/editobj.hxx                                 |    4 
 include/oox/export/chartexport.hxx                          |    4 
 oox/inc/drawingml/textcharacterproperties.hxx               |    8 
 oox/source/drawingml/chart/titleconverter.cxx               |   11 +
 oox/source/drawingml/textcharacterpropertiescontext.cxx     |    3 
 oox/source/export/chartexport.cxx                           |   81 +++++++---
 oox/source/export/drawingml.cxx                             |    3 
 sc/source/filter/xcl97/xcl97rec.cxx                         |    3 
 sc/source/ui/Accessibility/AccessiblePageHeader.cxx         |    2 
 sc/source/ui/view/viewfunc.cxx                              |    2 
 sd/source/filter/ppt/pptinanimations.cxx                    |    2 
 svx/source/svdraw/svdotxat.cxx                              |   19 +-
 svx/source/table/cell.cxx                                   |    2 
 sw/source/uibase/docvw/AnnotationWin.cxx                    |    2 
 xmloff/source/chart/SchXMLAxisContext.cxx                   |    1 
 xmloff/source/chart/SchXMLChartContext.cxx                  |    2 
 xmloff/source/chart/SchXMLExport.cxx                        |    3 
 39 files changed, 492 insertions(+), 127 deletions(-)

New commits:
commit 170fdbae18fdaf1ce0259b51f80566dc54f5a683
Author:     Balazs Varga <[email protected]>
AuthorDate: Fri Mar 29 23:00:50 2024 +0100
Commit:     Balazs Varga <[email protected]>
CommitDate: Wed Apr 3 11:09:23 2024 +0200

    Related: tdf#39052 - chart ooxml: export formatted chart titles
    
    texts properly to ooxml.
    
    Also adding "FormattedStrings" property for title objects
    to simplify the working of character formattings in editable
    chart shapes.
    
    TODO: odf import/export
    
    cherry-pick from commit: 55e9a27afd2d6a13cf76b39641bf121c3ec4b45c
    
    Change-Id: Ie27b4dee72c24fa6a2a4e2a7db8da7fa50eb8937
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/165583
    Tested-by: Jenkins
    Tested-by: Gabor Kelemen <[email protected]>
    Reviewed-by: Balazs Varga <[email protected]>

diff --git a/chart2/qa/extras/chart2export3.cxx 
b/chart2/qa/extras/chart2export3.cxx
index 845e6c98c6a6..b81c99218cba 100644
--- a/chart2/qa/extras/chart2export3.cxx
+++ b/chart2/qa/extras/chart2export3.cxx
@@ -455,12 +455,13 @@ CPPUNIT_TEST_FIXTURE(Chart2ExportTest3, 
testChartMainWithSubTitle)
     xmlDocUniquePtr pXmlDoc = parseExport("xl/charts/chart1.xml");
     CPPUNIT_ASSERT(pXmlDoc);
     // test properties of title
-    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p/a:pPr/a:defRPr", "sz", "1300");
-    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p/a:pPr/a:defRPr", "b", "0");
-    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p/a:pPr/a:defRPr", "i", "1");
-    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p/a:pPr/a:defRPr/a:solidFill/a:srgbClr",
 "val", "f10d0c");
-    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p/a:r/a:rPr/a:latin", "typeface", 
"Arial");
-    assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p/a:r/a:t", "It is a Maintitle
It is a Subtitle");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:pPr/a:defRPr", "sz", 
"1300");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:pPr/a:defRPr", "b", "0");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:pPr/a:defRPr", "i", "1");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:pPr/a:defRPr/a:solidFill/a:srgbClr",
 "val", "f10d0c");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r/a:rPr/a:latin", 
"typeface", "Arial");
+    assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r/a:t", "It is a 
Maintitle");
+    assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r/a:t", "It is a Subtitle");
     assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:spPr/a:solidFill/a:srgbClr", "val", "81d41a");
 }
 
@@ -741,6 +742,63 @@ CPPUNIT_TEST_FIXTURE(Chart2ExportTest3, testTdf148142)
     CPPUNIT_ASSERT(!aScaleData2.ShiftedCategoryPosition);
 }
 
+CPPUNIT_TEST_FIXTURE(Chart2ExportTest3, testFormattedChartTitles)
+{
+    loadFromFile(u"xlsx/tdf39052.xlsx");
+    save("Calc Office Open XML");
+    xmlDocUniquePtr pXmlDoc = parseExport("xl/charts/chart1.xml");
+    CPPUNIT_ASSERT(pXmlDoc);
+
+    // Check run level properties [1] - first paragraph
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[1]/a:rPr", "b", "1");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[1]/a:rPr", "sz", "1400");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[1]/a:rPr/a:solidFill/a:srgbClr",
 "val", "ff0000");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[1]/a:rPr/a:latin", 
"typeface", "Aptos Narrow");
+    assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[1]/a:t", "This");
+    // Check run level properties [2]
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[2]/a:rPr", "b", "0");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[2]/a:rPr", "sz", "1400");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[2]/a:rPr/a:solidFill/a:srgbClr",
 "val", "595959");
+    assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[2]/a:t", " is");
+    // Check run level properties [3]
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[3]/a:rPr", "b", "0");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[3]/a:rPr", "sz", "1400");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[3]/a:rPr", "baseline", 
"30000");
+    assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[3]/a:t", "3");
+    // Check run level properties [4]
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[4]/a:rPr", "b", "0");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[4]/a:rPr", "sz", "1400");
+    assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[4]/a:t", " a ");
+    // Check run level properties [5]
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[5]/a:rPr", "b", "0");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[5]/a:rPr", "i", "1");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[5]/a:rPr", "sz", "2000");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[5]/a:rPr", "u", "sng");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[5]/a:rPr/a:solidFill/a:srgbClr",
 "val", "4ea72e");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[5]/a:rPr/a:uFillTx", 1);
+    assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[5]/a:t", "custom");
+    // Check run level properties [6]
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[6]/a:rPr", "b", "0");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[6]/a:rPr", "sz", "1400");
+    assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[1]/a:r[6]/a:t", " erte1");
+    // Check run level properties [1] - second paragraph
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r[1]/a:rPr", "b", "0");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r[1]/a:rPr", "sz", "1400");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r[1]/a:rPr/a:solidFill/a:srgbClr",
 "val", "595959");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r[1]/a:rPr/a:latin", 
"typeface", "Aptos Narrow");
+    assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r[1]/a:t", "2dfgd ch");
+    // Check run level properties [2]
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r[2]/a:rPr", "b", "1");
+    assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r[2]/a:t", "ar");
+    // Check run level properties [3]
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r[3]/a:rPr", "b", "0");;
+    assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r[3]/a:t", "t ");
+    // Check run level properties [4]
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r[4]/a:rPr", "b", "0");
+    assertXPath(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r[4]/a:rPr", "strike", 
"sngStrike");
+    assertXPathContent(pXmlDoc, 
"/c:chartSpace/c:chart/c:title/c:tx/c:rich/a:p[2]/a:r[4]/a:t", "title");
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/chart2/qa/extras/data/xlsx/tdf39052.xlsx 
b/chart2/qa/extras/data/xlsx/tdf39052.xlsx
new file mode 100644
index 000000000000..5b0285bab292
Binary files /dev/null and b/chart2/qa/extras/data/xlsx/tdf39052.xlsx differ
diff --git a/chart2/source/controller/chartapiwrapper/TitleWrapper.cxx 
b/chart2/source/controller/chartapiwrapper/TitleWrapper.cxx
index 75d6c9e98f1f..6036869947e8 100644
--- a/chart2/source/controller/chartapiwrapper/TitleWrapper.cxx
+++ b/chart2/source/controller/chartapiwrapper/TitleWrapper.cxx
@@ -104,6 +104,63 @@ Any WrappedTitleStringProperty::getPropertyDefault( const 
Reference< beans::XPro
     return uno::Any( OUString() );//default title is an empty String
 }
 
+namespace {
+
+    class WrappedTitleFormStringsProperty : public WrappedProperty
+    {
+        public:
+            explicit WrappedTitleFormStringsProperty();
+
+            virtual void setPropertyValue( const Any& rOuterValue, const 
Reference< beans::XPropertySet >& xInnerPropertySet ) const override;
+            virtual Any getPropertyValue( const Reference< beans::XPropertySet 
>& xInnerPropertySet ) const override;
+            virtual Any getPropertyDefault( const Reference< 
beans::XPropertyState >& xInnerPropertyState ) const override;
+
+        protected:
+            Reference< uno::XComponentContext > m_xContext;
+    };
+
+}
+
+WrappedTitleFormStringsProperty::WrappedTitleFormStringsProperty()
+    : ::chart::WrappedProperty( "FormattedStrings", OUString() )
+{
+}
+
+void WrappedTitleFormStringsProperty::setPropertyValue( const Any& 
rOuterValue, const Reference< beans::XPropertySet >& xInnerPropertySet ) const
+{
+    Title* pTitle = dynamic_cast<Title*>(xInnerPropertySet.get());
+    if (pTitle)
+    {
+        Sequence< Reference< chart2::XFormattedString >> xFormattedStrings;
+        rOuterValue >>= xFormattedStrings;
+        TitleHelper::setFormattedString(pTitle, xFormattedStrings);
+    }
+}
+Any WrappedTitleFormStringsProperty::getPropertyValue( const Reference< 
beans::XPropertySet >& xInnerPropertySet ) const
+{
+    Any aRet(getPropertyDefault(Reference< beans::XPropertyState 
>(xInnerPropertySet, uno::UNO_QUERY)));
+    Reference< chart2::XTitle > xTitle(xInnerPropertySet, uno::UNO_QUERY);
+    if (xTitle.is())
+    {
+        const Sequence< Reference< chart2::XFormattedString > > 
aStrings(xTitle->getText());
+
+        OUStringBuffer aBuf;
+        for (Reference< chart2::XFormattedString > const& formattedStr : 
aStrings)
+        {
+            aBuf.append(formattedStr->getString());
+        }
+        if (!aBuf.makeStringAndClear().isEmpty())
+        {
+            aRet <<= aStrings;
+        }
+    }
+    return aRet;
+}
+Any WrappedTitleFormStringsProperty::getPropertyDefault( const Reference< 
beans::XPropertyState >& /*xInnerPropertyState*/ ) const
+{
+    return uno::Any(Sequence< Reference< chart2::XFormattedString > >()); 
//default title is an empty Sequence of XFormattedStrings
+}
+
 namespace {
 
 class WrappedStackedTextProperty : public WrappedProperty
@@ -127,6 +184,7 @@ namespace
 enum
 {
     PROP_TITLE_STRING,
+    PROP_TITLE_FORMATTED_STRINGS,
     PROP_TITLE_TEXT_ROTATION,
     PROP_TITLE_TEXT_STACKED
 };
@@ -139,7 +197,11 @@ void lcl_AddPropertiesToVector(
                   cppu::UnoType<OUString>::get(),
                   beans::PropertyAttribute::BOUND
                   | beans::PropertyAttribute::MAYBEVOID );
-
+    rOutProperties.emplace_back( "FormattedStrings",
+                  PROP_TITLE_FORMATTED_STRINGS,
+                  cppu::UnoType< Sequence< Reference< chart2::XFormattedString 
>>>::get(),
+                  beans::PropertyAttribute::BOUND
+                  | beans::PropertyAttribute::MAYBEVOID );
     rOutProperties.emplace_back( "TextRotation",
                   PROP_TITLE_TEXT_ROTATION,
                   cppu::UnoType<sal_Int32>::get(),
@@ -468,6 +530,7 @@ std::vector< std::unique_ptr<WrappedProperty> > 
TitleWrapper::createWrappedPrope
     std::vector< std::unique_ptr<WrappedProperty> > aWrappedProperties;
 
     aWrappedProperties.emplace_back( new WrappedTitleStringProperty( 
m_spChart2ModelContact->m_xContext ) );
+    aWrappedProperties.emplace_back( new WrappedTitleFormStringsProperty() );
     aWrappedProperties.emplace_back( new WrappedTextRotationProperty( true ) );
     aWrappedProperties.emplace_back( new WrappedStackedTextProperty() );
     WrappedCharacterHeightProperty::addWrappedProperties( aWrappedProperties, 
this );
diff --git a/chart2/source/controller/main/ChartController_TextEdit.cxx 
b/chart2/source/controller/main/ChartController_TextEdit.cxx
index d43549fcb81e..3b808af4522b 100644
--- a/chart2/source/controller/main/ChartController_TextEdit.cxx
+++ b/chart2/source/controller/main/ChartController_TextEdit.cxx
@@ -125,17 +125,10 @@ bool ChartController::EndTextEdit()
     if(!pTextObject)
         return false;
 
-    SdrOutliner* pOutliner = m_pDrawViewWrapper->getOutliner();
     OutlinerParaObject* pParaObj = pTextObject->GetOutlinerParaObject();
-    if( !pParaObj || !pOutliner )
+    if( !pParaObj )
         return true;
 
-    pOutliner->SetText( *pParaObj );
-
-    OUString aString = pOutliner->GetText(
-                        pOutliner->GetParagraph( 0 ),
-                        pOutliner->GetParagraphCount() );
-
     OUString aObjectCID = m_aSelection.getSelectedCID();
     if ( !aObjectCID.isEmpty() )
     {
@@ -149,26 +142,7 @@ bool ChartController::EndTextEdit()
             GetFormattedTitle(pParaObj->GetTextObject(), 
pTextObject->getUnoShape());
 
         Title* pTitle = dynamic_cast<Title*>(xPropSet.get());
-        if (pTitle && aNewFormattedTitle.hasElements())
-        {
-            bool bStacked = false;
-            if (xPropSet.is())
-                xPropSet->getPropertyValue("StackCharacters") >>= bStacked;
-
-            if (bStacked)
-            {
-                for (uno::Reference< chart2::XFormattedString >const& 
formattedStr : aNewFormattedTitle)
-                {
-                    
formattedStr->setString(TitleHelper::getUnstackedStr(formattedStr->getString()));
-                }
-            }
-
-            pTitle->setText(aNewFormattedTitle);
-        }
-        else
-        {
-            TitleHelper::setCompleteString(aString, pTitle, m_xCC);
-        }
+        TitleHelper::setFormattedString(pTitle, aNewFormattedTitle);
 
         OSL_ENSURE(m_pTextActionUndoGuard, "ChartController::EndTextEdit: no 
TextUndoGuard!");
         if (m_pTextActionUndoGuard)
diff --git a/chart2/source/inc/TitleHelper.hxx 
b/chart2/source/inc/TitleHelper.hxx
index a0940d7b4166..ba14ac36fdfb 100644
--- a/chart2/source/inc/TitleHelper.hxx
+++ b/chart2/source/inc/TitleHelper.hxx
@@ -78,6 +78,8 @@ public:
 
     static OUString getCompleteString( const rtl::Reference< ::chart::Title >& 
xTitle );
     static OUString getUnstackedStr( const OUString& rNewText );
+    static void setFormattedString( const rtl::Reference< ::chart::Title >& 
xTitle,
+        const css::uno::Sequence< css::uno::Reference< 
css::chart2::XFormattedString > >& aNewFormattedTitle );
     static void setCompleteString( const OUString& rNewText
         , const rtl::Reference< ::chart::Title >& xTitle
         , const css::uno::Reference< css::uno::XComponentContext > & xContext
diff --git a/chart2/source/tools/TitleHelper.cxx 
b/chart2/source/tools/TitleHelper.cxx
index a3831d332c30..5700d6e00449 100644
--- a/chart2/source/tools/TitleHelper.cxx
+++ b/chart2/source/tools/TitleHelper.cxx
@@ -329,32 +329,47 @@ OUString TitleHelper::getUnstackedStr(const OUString& 
rNewText)
     return aUnstackedStr.makeStringAndClear();
 }
 
+void TitleHelper::setFormattedString( const rtl::Reference< Title >& xTitle,
+    const css::uno::Sequence< css::uno::Reference< 
css::chart2::XFormattedString > >& aNewFormattedTitle )
+{
+    if (!xTitle.is() || !aNewFormattedTitle.hasElements())
+        return;
+
+    bool bStacked = false;
+    xTitle->getPropertyValue("StackCharacters") >>= bStacked;
+
+    if (bStacked)
+    {
+        for (uno::Reference< chart2::XFormattedString >const& formattedStr : 
aNewFormattedTitle)
+        {
+            
formattedStr->setString(TitleHelper::getUnstackedStr(formattedStr->getString()));
+        }
+    }
+
+    xTitle->setText(aNewFormattedTitle);
+}
+
 void TitleHelper::setCompleteString( const OUString& rNewText
                     , const rtl::Reference< Title >& xTitle
                     , const uno::Reference< uno::XComponentContext > & xContext
                     , const float * pDefaultCharHeight /* = 0 */
                     , bool bDialogTitle /*= false*/ )
 {
-    if(!xTitle.is())
+    if (!xTitle.is())
         return;
 
-    OUString aNewText = rNewText;
-
     bool bStacked = false;
     if( xTitle.is() )
         xTitle->getPropertyValue( "StackCharacters" ) >>= bStacked;
 
-    uno::Sequence< uno::Reference< XFormattedString > > aOldStringList = 
xTitle->getText();
+    OUString aNewText = rNewText;
     if( bStacked )
     {
         aNewText = getUnstackedStr(rNewText);
-        for (uno::Reference< XFormattedString >const & formattedStr : 
aOldStringList)
-        {
-            
formattedStr->setString(getUnstackedStr(formattedStr->getString()));
-        }
     }
 
     uno::Sequence< uno::Reference< XFormattedString > > aNewStringList;
+    uno::Sequence< uno::Reference< XFormattedString > > aOldStringList = 
xTitle->getText();
     if( aOldStringList.hasElements())
     {
         const OUString aFullString = getCompleteString(xTitle);
diff --git a/include/oox/export/chartexport.hxx 
b/include/oox/export/chartexport.hxx
index 6a40254f6491..4108aec96806 100644
--- a/include/oox/export/chartexport.hxx
+++ b/include/oox/export/chartexport.hxx
@@ -25,6 +25,7 @@
 
 #include <com/sun/star/uno/Reference.hxx>
 #include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/chart2/FormattedString.hpp>
 #include <oox/dllapi.h>
 #include <oox/export/drawingml.hxx>
 #include <oox/export/shapes.hxx>
@@ -176,7 +177,8 @@ private:
     void exportLegend( const css::uno::Reference<
                           css::chart::XChartDocument >& rChartDoc );
     void exportTitle( const css::uno::Reference< css::drawing::XShape >& 
xShape,
-                          const OUString* pSubText = nullptr );
+                          const css::uno::Sequence< css::uno::Reference< 
css::chart2::XFormattedString > >& xFormattedSubTitle =
+                          css::uno::Sequence< css::uno::Reference< 
css::chart2::XFormattedString > >() );
     void exportPlotArea( const css::uno::Reference<
                              css::chart::XChartDocument >& rChartDoc );
     void exportAdditionalShapes( const 
css::uno::Reference<css::chart::XChartDocument >& rChartDoc );
diff --git a/oox/source/export/chartexport.cxx 
b/oox/source/export/chartexport.cxx
index c9db6f8cc431..1837b67cffed 100644
--- a/oox/source/export/chartexport.cxx
+++ b/oox/source/export/chartexport.cxx
@@ -1129,7 +1129,6 @@ void ChartExport::exportChart( const Reference< 
css::chart::XChartDocument >& xC
 
     // get Properties of ChartDocument
     bool bHasMainTitle = false;
-    OUString aSubTitle;
     bool bHasLegend = false;
     Reference< beans::XPropertySet > xDocPropSet( xChartDoc, uno::UNO_QUERY );
     if( xDocPropSet.is())
@@ -1147,12 +1146,13 @@ void ChartExport::exportChart( const Reference< 
css::chart::XChartDocument >& xC
         }
     } // if( xDocPropSet.is())
 
+    Sequence< uno::Reference< chart2::XFormattedString > > xFormattedSubTitle;
     Reference< beans::XPropertySet > xPropSubTitle( xChartDoc->getSubTitle(), 
UNO_QUERY );
     if( xPropSubTitle.is())
     {
         try
         {
-            xPropSubTitle->getPropertyValue("String") >>= aSubTitle;
+            xPropSubTitle->getPropertyValue("FormattedStrings") >>= 
xFormattedSubTitle;
         }
         catch( beans::UnknownPropertyException & )
         {
@@ -1166,12 +1166,12 @@ void ChartExport::exportChart( const Reference< 
css::chart::XChartDocument >& xC
     // titles
     if( bHasMainTitle )
     {
-        exportTitle( xChartDoc->getTitle(), !aSubTitle.isEmpty() ? &aSubTitle 
: nullptr );
+        exportTitle( xChartDoc->getTitle(), xFormattedSubTitle);
         pFS->singleElement(FSNS(XML_c, XML_autoTitleDeleted), XML_val, "0");
     }
-    else if( !aSubTitle.isEmpty() )
+    else if( xFormattedSubTitle.hasElements() )
     {
-        exportTitle( xChartDoc->getSubTitle(), nullptr );
+        exportTitle( xChartDoc->getSubTitle() );
         pFS->singleElement(FSNS(XML_c, XML_autoTitleDeleted), XML_val, "0");
     }
     else
@@ -1440,20 +1440,40 @@ void ChartExport::exportLegend( const Reference< 
css::chart::XChartDocument >& x
     pFS->endElement( FSNS( XML_c, XML_legend ) );
 }
 
-void ChartExport::exportTitle( const Reference< XShape >& xShape, const 
OUString* pSubText)
+void ChartExport::exportTitle( const Reference< XShape >& xShape,
+    const css::uno::Sequence< uno::Reference< css::chart2::XFormattedString > 
>& xFormattedSubTitle )
 {
-    OUString sText;
+    Sequence< uno::Reference< chart2::XFormattedString > > xFormattedTitle;
     Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY );
     if( xPropSet.is())
     {
-        xPropSet->getPropertyValue("String") >>= sText;
+        xPropSet->getPropertyValue("FormattedStrings") >>= xFormattedTitle;
     }
 
     // tdf#101322: add subtitle to title
-    if( pSubText )
-        sText = sText.isEmpty() ? *pSubText : sText + "
" + *pSubText;
+    if (xFormattedSubTitle.hasElements())
+    {
+        if (!xFormattedTitle.hasElements())
+        {
+            xFormattedTitle = xFormattedSubTitle;
+        }
+        else
+        {
+            sal_uInt32 nLength = xFormattedTitle.size();
+            const OUString aLastString = xFormattedTitle.getArray()[nLength - 
1]->getString();
+            xFormattedTitle.getArray()[nLength - 1]->setString(aLastString + 
OUStringChar('
'));
+            for (const uno::Reference<chart2::XFormattedString>& rxFS : 
xFormattedSubTitle)
+            {
+                if (!rxFS->getString().isEmpty())
+                {
+                    xFormattedTitle.realloc(nLength + 1);
+                    xFormattedTitle.getArray()[nLength++] = rxFS;
+                }
+            }
+        }
+    }
 
-    if( sText.isEmpty() )
+    if (!xFormattedTitle.hasElements())
         return;
 
     FSHelperPtr pFS = GetFS();
@@ -1477,7 +1497,6 @@ void ChartExport::exportTitle( const Reference< XShape >& 
xShape, const OUString
             XML_rot, oox::drawingml::calcRotationValue(nRotation) );
     // TODO: lstStyle
     pFS->singleElement(FSNS(XML_a, XML_lstStyle));
-    // FIXME: handle multiple paragraphs to parse aText
     pFS->startElement(FSNS(XML_a, XML_p));
 
     pFS->startElement(FSNS(XML_a, XML_pPr));
@@ -1488,13 +1507,37 @@ void ChartExport::exportTitle( const Reference< XShape 
>& xShape, const OUString
 
     pFS->endElement( FSNS( XML_a, XML_pPr ) );
 
-    pFS->startElement(FSNS(XML_a, XML_r));
-    bDummy = false;
-    WriteRunProperties( xPropSet, false, XML_rPr, true, bDummy, nDummy );
-    pFS->startElement(FSNS(XML_a, XML_t));
-    pFS->writeEscaped( sText );
-    pFS->endElement( FSNS( XML_a, XML_t ) );
-    pFS->endElement( FSNS( XML_a, XML_r ) );
+    for (const uno::Reference<chart2::XFormattedString>& rxFS : 
xFormattedTitle)
+    {
+        pFS->startElement(FSNS(XML_a, XML_r));
+        bDummy = false;
+        Reference< beans::XPropertySet > xRunPropSet(rxFS, uno::UNO_QUERY);
+        WriteRunProperties(xRunPropSet, false, XML_rPr, true, bDummy, nDummy);
+        pFS->startElement(FSNS(XML_a, XML_t));
+
+        // the linebreak should always be at the end of the XFormattedString 
text
+        bool bNextPara = rxFS->getString().endsWith(u"
");
+        if (!bNextPara)
+            pFS->writeEscaped(rxFS->getString());
+        else
+        {
+            sal_Int32 nEnd = rxFS->getString().lastIndexOf('
');
+            pFS->writeEscaped(rxFS->getString().replaceAt(nEnd, 1, u""));
+        }
+        pFS->endElement(FSNS(XML_a, XML_t));
+        pFS->endElement(FSNS(XML_a, XML_r));
+
+        if (bNextPara)
+        {
+            pFS->endElement(FSNS(XML_a, XML_p));
+
+            pFS->startElement(FSNS(XML_a, XML_p));
+            pFS->startElement(FSNS(XML_a, XML_pPr));
+            bDummy = false;
+            WriteRunProperties(xPropSet, false, XML_defRPr, true, bDummy, 
nDummy);
+            pFS->endElement(FSNS(XML_a, XML_pPr));
+        }
+    }
 
     pFS->endElement( FSNS( XML_a, XML_p ) );
 
diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx
index e021354a4f0a..296bf5011354 100644
--- a/oox/source/export/drawingml.cxx
+++ b/oox/source/export/drawingml.cxx
@@ -2407,6 +2407,9 @@ void DrawingML::WriteRunProperties( const Reference< 
XPropertySet >& rRun, bool
     {
         switch ( *o3tl::doAccess<sal_Int16>(mAny) )
         {
+            case awt::FontUnderline::NONE :
+                underline = "none";
+                break;
             case awt::FontUnderline::SINGLE :
                 underline = "sng";
                 break;
diff --git a/xmloff/source/chart/SchXMLAxisContext.cxx 
b/xmloff/source/chart/SchXMLAxisContext.cxx
index ae55da90f8b5..fd106f72fa69 100644
--- a/xmloff/source/chart/SchXMLAxisContext.cxx
+++ b/xmloff/source/chart/SchXMLAxisContext.cxx
@@ -565,6 +565,7 @@ void SchXMLAxisContext::SetAxisTitle()
     {
         try
         {
+            // TODO: ODF import for formatted chart titles
             xTitleProp->setPropertyValue("String", 
uno::Any(m_aCurrentAxis.aTitle) );
         }
         catch( beans::UnknownPropertyException & )
diff --git a/xmloff/source/chart/SchXMLChartContext.cxx 
b/xmloff/source/chart/SchXMLChartContext.cxx
index 7a8ac46c40a9..6d1350add627 100644
--- a/xmloff/source/chart/SchXMLChartContext.cxx
+++ b/xmloff/source/chart/SchXMLChartContext.cxx
@@ -727,6 +727,7 @@ void SchXMLChartContext::endFastElement(sal_Int32 )
             {
                 try
                 {
+                    // TODO: ODF import for formatted chart titles
                     xTitleProp->setPropertyValue("String", 
uno::Any(maMainTitle) );
                 }
                 catch(const beans::UnknownPropertyException&)
@@ -742,6 +743,7 @@ void SchXMLChartContext::endFastElement(sal_Int32 )
             {
                 try
                 {
+                    // TODO: ODF import for formatted chart titles
                     xTitleProp->setPropertyValue("String", 
uno::Any(maSubTitle) );
                 }
                 catch(const beans::UnknownPropertyException&)
diff --git a/xmloff/source/chart/SchXMLExport.cxx 
b/xmloff/source/chart/SchXMLExport.cxx
index e07fb6a77693..e1eb8f21a164 100644
--- a/xmloff/source/chart/SchXMLExport.cxx
+++ b/xmloff/source/chart/SchXMLExport.cxx
@@ -1336,6 +1336,7 @@ void SchXMLExportHelper_Impl::parseDocument( Reference< 
chart::XChartDocument >
             Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY 
);
             if( xPropSet.is())
             {
+                // TODO: ODF export for formatted chart titles
                 Any aAny( xPropSet->getPropertyValue( "String" ));
                 OUString aText;
                 aAny >>= aText;
@@ -1377,6 +1378,7 @@ void SchXMLExportHelper_Impl::parseDocument( Reference< 
chart::XChartDocument >
             Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY 
);
             if( xPropSet.is())
             {
+                // TODO: ODF import for formatted chart titles
                 Any aAny( xPropSet->getPropertyValue( "String" ));
                 OUString aText;
                 aAny >>= aText;
@@ -2256,6 +2258,7 @@ void SchXMLExportHelper_Impl::exportAxisTitle( const 
Reference< beans::XProperty
     std::vector<XMLPropertyState> aPropertyStates = 
mxExpPropMapper->Filter(mrExport, rTitleProps);
     if( bExportContent )
     {
+        // TODO: ODF import for formatted chart titles
         OUString aText;
         Any aAny( rTitleProps->getPropertyValue( "String" ));
         aAny >>= aText;
commit f4eaa12e2bd6018f13904dd977b92de60851621c
Author:     Balazs Varga <[email protected]>
AuthorDate: Thu Mar 28 17:55:30 2024 +0100
Commit:     Balazs Varga <[email protected]>
CommitDate: Wed Apr 3 10:38:07 2024 +0200

    tdf#39052 - Chart: make characters formatable in editable chart textshapes
    
    Editable textshapes include main chart title, sub chart title, axis titles.
    
    In chart2 the chart2::XFormattedString and chart2::XFormattedString2 store
    the formatted characters from textshapes, so we need to set all the 
character
    properties from the EditTextObject and need to add them to to the 
XFormattedString
    array with all the related texts which are formatted individually.
    
    For formatting of the characters the .uno:FontDialog command can be used,
    which can be called from the chart menubar, toolbar and with right click
    on the text when we are in edit mode in the textshape.
    
    (Note: in the next patch the OOXML export will be fixed.)
    
    cherry-pick from commit: 4f994cec388377cc5c2bddb804bd92eb4cd7dc8d
    
    Change-Id: I5750a5fe694b384dc6b28e2ef03ac1f2b03957db
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/165501
    Tested-by: Jenkins
    Tested-by: Gabor Kelemen <[email protected]>
    Reviewed-by: Balazs Varga <[email protected]>

diff --git a/chart2/source/controller/dialogs/TitleDialogData.cxx 
b/chart2/source/controller/dialogs/TitleDialogData.cxx
index f110b3d520bd..e711ad151e75 100644
--- a/chart2/source/controller/dialogs/TitleDialogData.cxx
+++ b/chart2/source/controller/dialogs/TitleDialogData.cxx
@@ -100,7 +100,7 @@ bool TitleDialogData::writeDifferenceToModel(
                 TitleHelper::getTitle( static_cast< TitleHelper::eTitleType >( 
nN ), xChartModel ) );
             if(xTitle.is())
             {
-                TitleHelper::setCompleteString( aTextList[nN], xTitle, 
xContext );
+                TitleHelper::setCompleteString( aTextList[nN], xTitle, 
xContext, nullptr, true );
                 bChanged = true;
             }
         }
diff --git a/chart2/source/controller/inc/ChartController.hxx 
b/chart2/source/controller/inc/ChartController.hxx
index 63fb4d4fc302..8535bcc8c0d0 100644
--- a/chart2/source/controller/inc/ChartController.hxx
+++ b/chart2/source/controller/inc/ChartController.hxx
@@ -330,6 +330,7 @@ public:
     void setDrawMode( ChartDrawMode eMode ) { m_eDrawMode = eMode; }
 
     bool isShapeContext() const;
+    bool IsTextEdit() const;
 
     ViewElementListProvider getViewElementListProvider();
     DrawModelWrapper* GetDrawModelWrapper();
@@ -491,6 +492,8 @@ private:
     void executeDispatch_MoveSeries( bool bForward );
 
     bool EndTextEdit();
+    css::uno::Sequence< css::uno::Reference<css::chart2::XFormattedString >> 
GetFormattedTitle(
+        const EditTextObject& aEdit, const css::uno::Reference< 
css::drawing::XShape >& xShape );
 
     void executeDispatch_View3D();
     void executeDispatch_PositionAndSize( const ::css::uno::Sequence< 
::css::beans::PropertyValue >* pArgs = nullptr );
@@ -521,7 +524,7 @@ private:
         const css::uno::Sequence< css::beans::PropertyValue >& rArgs );
 
     DECL_LINK( DoubleClickWaitingHdl, Timer*, void );
-    void execute_DoubleClick( const Point* pMousePixel );
+    void execute_DoubleClick( const Point* pMousePixel, bool &bEditText );
     void startDoubleClickWaiting();
     void stopDoubleClickWaiting();
 
diff --git a/chart2/source/controller/inc/SelectionHelper.hxx 
b/chart2/source/controller/inc/SelectionHelper.hxx
index ff0e95eee27a..119640bd4300 100644
--- a/chart2/source/controller/inc/SelectionHelper.hxx
+++ b/chart2/source/controller/inc/SelectionHelper.hxx
@@ -39,6 +39,7 @@ public: //methods
 
     bool isResizeableObjectSelected() const;
     bool isRotateableObjectSelected( const 
rtl::Reference<::chart::ChartModel>& xChartModel ) const;
+    bool isTitleObjectSelected() const;
     bool isDragableObjectSelected() const;
 
     bool isAdditionalShapeSelected() const;
diff --git a/chart2/source/controller/main/ChartController.cxx 
b/chart2/source/controller/main/ChartController.cxx
index 3f94410d22b0..e105885e04ad 100644
--- a/chart2/source/controller/main/ChartController.cxx
+++ b/chart2/source/controller/main/ChartController.cxx
@@ -1259,7 +1259,9 @@ void SAL_CALL ChartController::dispatch(
             this->executeDispatch_PositionAndSize();
         }
     }
-    else if( lcl_isFormatObjectCommand(aCommand) )
+    else if ( aCommand == "FontDialog" )
+        this->impl_ShapeControllerDispatch(rURL, rArgs);
+    else if (lcl_isFormatObjectCommand(aCommand))
         this->executeDispatch_FormatObject(rURL.Path);
     //more format
     else if( aCommand == "DiagramType" )
@@ -1655,7 +1657,7 @@ const o3tl::sorted_vector< OUString >& 
ChartController::impl_getAvailableCommand
         "InsertMenuDataTable",
         "InsertDataTable", "DeleteDataTable",
         //format objects
-        "FormatSelection",     "TransformDialog",
+        "FormatSelection",    "FontDialog",           "TransformDialog",
         "DiagramType",        "View3D",
         "Forward",            "Backward",
         "MainTitle",          "SubTitle",
diff --git a/chart2/source/controller/main/ChartController_TextEdit.cxx 
b/chart2/source/controller/main/ChartController_TextEdit.cxx
index fbc8df4ed6b0..d43549fcb81e 100644
--- a/chart2/source/controller/main/ChartController_TextEdit.cxx
+++ b/chart2/source/controller/main/ChartController_TextEdit.cxx
@@ -30,6 +30,7 @@
 #include <TitleHelper.hxx>
 #include <ObjectIdentifier.hxx>
 #include <ControllerLockGuard.hxx>
+#include <comphelper/diagnose_ex.hxx>
 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
 #include <AccessibleTextHelper.hxx>
 #endif
@@ -42,10 +43,10 @@
 #include <svx/svxids.hrc>
 #include <editeng/editids.hrc>
 #include <vcl/svapp.hxx>
-#include <com/sun/star/beans/XPropertySet.hpp>
-#include <com/sun/star/chart2/XTitle.hpp>
+#include <com/sun/star/chart2/FormattedString.hpp>
 #include <svl/stritem.hxx>
 #include <editeng/fontitem.hxx>
+#include <editeng/section.hxx>
 #include <memory>
 
 namespace chart
@@ -144,8 +145,30 @@ bool ChartController::EndTextEdit()
         // lock controllers till end of block
         ControllerLockGuardUNO aCLGuard( getChartModel() );
 
+        uno::Sequence< uno::Reference< chart2::XFormattedString > > 
aNewFormattedTitle =
+            GetFormattedTitle(pParaObj->GetTextObject(), 
pTextObject->getUnoShape());
+
         Title* pTitle = dynamic_cast<Title*>(xPropSet.get());
-        TitleHelper::setCompleteString( aString, pTitle, m_xCC );
+        if (pTitle && aNewFormattedTitle.hasElements())
+        {
+            bool bStacked = false;
+            if (xPropSet.is())
+                xPropSet->getPropertyValue("StackCharacters") >>= bStacked;
+
+            if (bStacked)
+            {
+                for (uno::Reference< chart2::XFormattedString >const& 
formattedStr : aNewFormattedTitle)
+                {
+                    
formattedStr->setString(TitleHelper::getUnstackedStr(formattedStr->getString()));
+                }
+            }
+
+            pTitle->setText(aNewFormattedTitle);
+        }
+        else
+        {
+            TitleHelper::setCompleteString(aString, pTitle, m_xCC);
+        }
 
         OSL_ENSURE(m_pTextActionUndoGuard, "ChartController::EndTextEdit: no 
TextUndoGuard!");
         if (m_pTextActionUndoGuard)
@@ -155,6 +178,70 @@ bool ChartController::EndTextEdit()
     return true;
 }
 
+uno::Sequence< uno::Reference< chart2::XFormattedString > > 
ChartController::GetFormattedTitle(
+    const EditTextObject& aEdit, const uno::Reference< drawing::XShape >& 
xShape )
+{
+    std::vector < uno::Reference< chart2::XFormattedString > > aNewStrings;
+    if (!xShape.is())
+        return comphelper::containerToSequence(aNewStrings);
+
+    uno::Reference< text::XText > xText(xShape, uno::UNO_QUERY);
+    if (!xText.is())
+        return comphelper::containerToSequence(aNewStrings);
+
+    uno::Reference< text::XTextCursor > 
xSelectionCursor(xText->createTextCursor());
+    if (!xSelectionCursor.is())
+        return comphelper::containerToSequence(aNewStrings);
+
+    xSelectionCursor->gotoStart(false);
+
+    std::vector<editeng::Section> aSecAttrs;
+    aEdit.GetAllSections(aSecAttrs);
+
+    for (editeng::Section const& rSection : aSecAttrs)
+    {
+        if (!xSelectionCursor->isCollapsed())
+            xSelectionCursor->collapseToEnd();
+
+        xSelectionCursor->goRight(rSection.mnEnd - rSection.mnStart, true);
+
+        OUString aNewString = xSelectionCursor->getString();
+
+        bool bNextPara = (aEdit.GetParagraphCount() > 1 && 
rSection.mnParagraph != aEdit.GetParagraphCount() - 1 &&
+            aEdit.GetTextLen(rSection.mnParagraph) <= rSection.mnEnd);
+
+        uno::Reference< chart2::XFormattedString2 > xFmtStr = 
chart2::FormattedString::create(m_xCC);
+        if (bNextPara)
+            aNewString = aNewString + OUStringChar('
');
+        xFmtStr->setString(aNewString);
+        aNewStrings.emplace_back(xFmtStr);
+
+        uno::Reference< beans::XPropertySetInfo > xInfo = 
xFmtStr->getPropertySetInfo();
+        uno::Reference< beans::XPropertySet > xSelectionProp(xSelectionCursor, 
uno::UNO_QUERY);
+        try
+        {
+            for (const beans::Property& rProp : 
xSelectionProp->getPropertySetInfo()->getProperties())
+            {
+                if (xInfo.is() && xInfo->hasPropertyByName(rProp.Name))
+                {
+                    const uno::Any aValue = 
xSelectionProp->getPropertyValue(rProp.Name);
+                    xFmtStr->setPropertyValue(rProp.Name, aValue);
+                }
+            }
+        }
+        catch ( const uno::Exception& )
+        {
+            DBG_UNHANDLED_EXCEPTION("chart2");
+            aNewStrings.clear();
+        }
+
+        if (bNextPara)
+            xSelectionCursor->goRight(1, false); // next paragraph
+    }
+
+    return comphelper::containerToSequence(aNewStrings);
+}
+
 void ChartController::executeDispatch_InsertSpecialCharacter()
 {
     SolarMutexGuard aGuard;
diff --git a/chart2/source/controller/main/ChartController_Tools.cxx 
b/chart2/source/controller/main/ChartController_Tools.cxx
index da166261328f..ae9f3f4f47f4 100644
--- a/chart2/source/controller/main/ChartController_Tools.cxx
+++ b/chart2/source/controller/main/ChartController_Tools.cxx
@@ -320,8 +320,8 @@ void ChartController::executeDispatch_Paste()
                 if( m_pDrawViewWrapper )
                 {
                     OutlinerView* pOutlinerView = 
m_pDrawViewWrapper->GetTextEditOutlinerView();
-                    if( pOutlinerView )//in case of edit mode insert into 
edited string
-                        pOutlinerView->InsertText( aString );
+                    if (pOutlinerView)//in case of edit mode insert the 
formatted string
+                        pOutlinerView->PasteSpecial();
                     else
                     {
                         impl_PasteStringAsTextShape( aString, awt::Point( 0, 0 
) );
@@ -589,6 +589,13 @@ bool ChartController::isShapeContext() const
            ( m_pDrawViewWrapper->GetCurrentObjIdentifier() == SdrObjKind::Text 
) );
 }
 
+bool ChartController::IsTextEdit() const
+{
+    // only Title objects are editable textshapes
+    return m_aSelection.isTitleObjectSelected() &&
+        m_pDrawViewWrapper && m_pDrawViewWrapper->IsTextEdit();
+}
+
 void ChartController::impl_ClearSelection()
 {
     if( m_aSelection.hasSelection())
diff --git a/chart2/source/controller/main/ChartController_Window.cxx 
b/chart2/source/controller/main/ChartController_Window.cxx
index 1bdb1f2ed48a..4530c4b3e7a1 100644
--- a/chart2/source/controller/main/ChartController_Window.cxx
+++ b/chart2/source/controller/main/ChartController_Window.cxx
@@ -745,6 +745,7 @@ void ChartController::execute_MouseButtonUp( const 
MouseEvent& rMEvt )
     bool bMouseUpWithoutMouseDown = !m_bWaitingForMouseUp;
     m_bWaitingForMouseUp = false;
     bool bNotifySelectionChange = false;
+    bool bEditText = false;
     {
         SolarMutexGuard aGuard;
 
@@ -921,7 +922,7 @@ void ChartController::execute_MouseButtonUp( const 
MouseEvent& rMEvt )
         if( isDoubleClick(rMEvt) && !bMouseUpWithoutMouseDown /*#i106966#*/ )
         {
             Point aMousePixel = rMEvt.GetPosPixel();
-            execute_DoubleClick( &aMousePixel );
+            execute_DoubleClick( &aMousePixel, bEditText );
         }
 
         if( m_aSelection.isSelectionDifferentFromBeforeMouseDown() )
@@ -930,18 +931,17 @@ void ChartController::execute_MouseButtonUp( const 
MouseEvent& rMEvt )
 
     impl_SetMousePointer( rMEvt );
 
-    if(bNotifySelectionChange)
+    if(bNotifySelectionChange || bEditText)
         impl_notifySelectionChangeListeners();
 }
 
-void ChartController::execute_DoubleClick( const Point* pMousePixel )
+void ChartController::execute_DoubleClick( const Point* pMousePixel, bool 
&bEditText )
 {
     const SfxViewShell* pViewShell = SfxViewShell::Current();
     bool isMobilePhone = pViewShell && pViewShell->isLOKMobilePhone();
     if (isMobilePhone)
         return;
 
-    bool bEditText = false;
     if ( m_aSelection.hasSelection() )
     {
         OUString aCID( m_aSelection.getSelectedCID() );
@@ -1036,6 +1036,8 @@ void ChartController::execute_Command( const 
CommandEvent& rCEvt )
 
             OUString aFormatCommand( lcl_getFormatCommandForObjectCID( 
m_aSelection.getSelectedCID() ) );
             lcl_insertMenuCommand( xPopupMenu, nUniqueId++, aFormatCommand );
+            if (eObjectType == OBJECTTYPE_TITLE && 
m_pDrawViewWrapper->IsTextEdit())
+                lcl_insertMenuCommand( xPopupMenu, nUniqueId++, 
".uno:FontDialog" );
 
             //some commands for dataseries and points:
 
diff --git a/chart2/source/controller/main/ControllerCommandDispatch.cxx 
b/chart2/source/controller/main/ControllerCommandDispatch.cxx
index 98468a138c3f..9c9c0a898965 100644
--- a/chart2/source/controller/main/ControllerCommandDispatch.cxx
+++ b/chart2/source/controller/main/ControllerCommandDispatch.cxx
@@ -533,6 +533,7 @@ void ControllerCommandDispatch::updateCommandAvailability()
     // read-only
     bool bIsWritable = bModelStateIsValid && (! m_apModelState->bIsReadOnly);
     bool bShapeContext = m_xChartController.is() && 
m_xChartController->isShapeContext();
+    bool bIsTextEdit = m_xChartController.is() && 
m_xChartController->IsTextEdit();
 
     bool bEnableDataTableDialog = false;
     bool bCanCreateDataProvider = false;
@@ -599,9 +600,10 @@ void ControllerCommandDispatch::updateCommandAvailability()
 
     // format objects
     bool bFormatObjectAvailable = bIsWritable && bControllerStateIsValid && 
m_apControllerState->bIsFormateableObjectSelected;
-    m_aCommandAvailability[ ".uno:FormatSelection" ] = bFormatObjectAvailable;
+    m_aCommandAvailability[ ".uno:FormatSelection" ] = bFormatObjectAvailable 
&& !bIsTextEdit;
+    m_aCommandAvailability[ ".uno:FontDialog" ] = bFormatObjectAvailable && 
bIsTextEdit;
     m_aCommandAvailability[ ".uno:FormatAxis" ] = bFormatObjectAvailable;
-    m_aCommandAvailability[ ".uno:FormatTitle" ] = bFormatObjectAvailable;
+    m_aCommandAvailability[ ".uno:FormatTitle" ] = bFormatObjectAvailable && 
!bIsTextEdit;
     m_aCommandAvailability[ ".uno:FormatDataSeries" ] = bFormatObjectAvailable;
     m_aCommandAvailability[ ".uno:FormatDataPoint" ] = bFormatObjectAvailable;
     m_aCommandAvailability[ ".uno:FormatDataLabels" ] = bFormatObjectAvailable;
diff --git a/chart2/source/controller/main/SelectionHelper.cxx 
b/chart2/source/controller/main/SelectionHelper.cxx
index 11fc5d9faee7..8e7e1a1f7700 100644
--- a/chart2/source/controller/main/SelectionHelper.cxx
+++ b/chart2/source/controller/main/SelectionHelper.cxx
@@ -314,6 +314,11 @@ bool Selection::isDragableObjectSelected() const
     return m_aSelectedOID.isDragableObject();
 }
 
+bool Selection::isTitleObjectSelected() const
+{
+    return m_aSelectedOID.getObjectType() == OBJECTTYPE_TITLE;
+}
+
 bool Selection::isAdditionalShapeSelected() const
 {
     return m_aSelectedOID.isAdditionalShape();
diff --git a/chart2/source/controller/sidebar/ChartElementsPanel.cxx 
b/chart2/source/controller/sidebar/ChartElementsPanel.cxx
index 4f856355ba9c..3325b1d9e274 100644
--- a/chart2/source/controller/sidebar/ChartElementsPanel.cxx
+++ b/chart2/source/controller/sidebar/ChartElementsPanel.cxx
@@ -636,7 +636,8 @@ IMPL_LINK(ChartElementsPanel, EditHdl, weld::Entry&, rEdit, 
void)
 
     // set it
     OUString aText(rEdit.get_text());
-    TitleHelper::setCompleteString(aText, TitleHelper::getTitle(aTitleType, 
mxModel), comphelper::getProcessComponentContext());
+    TitleHelper::setCompleteString(aText, TitleHelper::getTitle(aTitleType, 
mxModel),
+        comphelper::getProcessComponentContext(), nullptr, true);
 }
 
 IMPL_LINK_NOARG(ChartElementsPanel, LegendPosHdl, weld::ComboBox&, void)
diff --git a/chart2/source/inc/TitleHelper.hxx 
b/chart2/source/inc/TitleHelper.hxx
index ac9f9618799b..a0940d7b4166 100644
--- a/chart2/source/inc/TitleHelper.hxx
+++ b/chart2/source/inc/TitleHelper.hxx
@@ -77,10 +77,12 @@ public:
                     , const rtl::Reference< ::chart::ChartModel >& xModel );
 
     static OUString getCompleteString( const rtl::Reference< ::chart::Title >& 
xTitle );
+    static OUString getUnstackedStr( const OUString& rNewText );
     static void setCompleteString( const OUString& rNewText
         , const rtl::Reference< ::chart::Title >& xTitle
         , const css::uno::Reference< css::uno::XComponentContext > & xContext
-        , const float * pDefaultCharHeight = nullptr );
+        , const float * pDefaultCharHeight = nullptr
+        , bool bDialogTitle = false );
 
     static bool getTitleType( eTitleType& rType
                     , const rtl::Reference< ::chart::Title >& xTitle
diff --git a/chart2/source/tools/TitleHelper.cxx 
b/chart2/source/tools/TitleHelper.cxx
index 992f5cdf6196..a3831d332c30 100644
--- a/chart2/source/tools/TitleHelper.cxx
+++ b/chart2/source/tools/TitleHelper.cxx
@@ -305,12 +305,36 @@ OUString TitleHelper::getCompleteString( const 
rtl::Reference< Title >& xTitle )
     return aRet.makeStringAndClear();
 }
 
+OUString TitleHelper::getUnstackedStr(const OUString& rNewText)
+{
+    //#i99841# remove linebreaks that were added for vertical stacking
+    OUStringBuffer aUnstackedStr;
+    OUStringBuffer aSource(rNewText);
+
+    bool bBreakIgnored = false;
+    sal_Int32 nLen = rNewText.getLength();
+    for (sal_Int32 nPos = 0; nPos < nLen; ++nPos)
+    {
+        sal_Unicode aChar = aSource[nPos];
+        if (aChar != '
')
+        {
+            aUnstackedStr.append(aChar);
+            bBreakIgnored = false;
+        }
+        else if (aChar == '
' && bBreakIgnored)
+            aUnstackedStr.append(aChar);
+        else
+            bBreakIgnored = true;
+    }
+    return aUnstackedStr.makeStringAndClear();
+}
+
 void TitleHelper::setCompleteString( const OUString& rNewText
                     , const rtl::Reference< Title >& xTitle
                     , const uno::Reference< uno::XComponentContext > & xContext
-                    , const float * pDefaultCharHeight /* = 0 */ )
+                    , const float * pDefaultCharHeight /* = 0 */
+                    , bool bDialogTitle /*= false*/ )
 {
-    //the format of the first old text portion will be maintained if there is 
any
     if(!xTitle.is())
         return;
 
@@ -320,37 +344,35 @@ void TitleHelper::setCompleteString( const OUString& 
rNewText
     if( xTitle.is() )
         xTitle->getPropertyValue( "StackCharacters" ) >>= bStacked;
 
+    uno::Sequence< uno::Reference< XFormattedString > > aOldStringList = 
xTitle->getText();
     if( bStacked )
     {
-        //#i99841# remove linebreaks that were added for vertical stacking
-        OUStringBuffer aUnstackedStr;
-        OUStringBuffer aSource(rNewText);
-
-        bool bBreakIgnored = false;
-        sal_Int32 nLen = rNewText.getLength();
-        for( sal_Int32 nPos = 0; nPos < nLen; ++nPos )
+        aNewText = getUnstackedStr(rNewText);
+        for (uno::Reference< XFormattedString >const & formattedStr : 
aOldStringList)
         {
-            sal_Unicode aChar = aSource[nPos];
-            if( aChar != '
' )
-            {
-                aUnstackedStr.append( aChar );
-                bBreakIgnored = false;
-            }
-            else if( aChar == '
' && bBreakIgnored )
-                aUnstackedStr.append( aChar );
-            else
-                bBreakIgnored = true;
+            
formattedStr->setString(getUnstackedStr(formattedStr->getString()));
         }
-        aNewText = aUnstackedStr.makeStringAndClear();
     }
 
     uno::Sequence< uno::Reference< XFormattedString > > aNewStringList;
-
-    uno::Sequence< uno::Reference< XFormattedString > >  aOldStringList = 
xTitle->getText();
-    if( aOldStringList.hasElements() )
+    if( aOldStringList.hasElements())
     {
-        aNewStringList = { aOldStringList[0] };
-        aNewStringList[0]->setString( aNewText );
+        const OUString aFullString = getCompleteString(xTitle);
+        if (bDialogTitle && aNewText.equals(getUnstackedStr(aFullString)))
+        {
+            // If the new title setted from a dialog window to a new string
+            // the first old text portion will be maintained if its a new 
string,
+            // otherwise we use the original one.
+            aNewStringList = aOldStringList;
+        }
+        else
+        {
+            // If the new title setted from a dialog to a new string the first
+            // old text portion will be maintained if there was any. Also in 
case of ODF
+            // import which still not support non-uniform formatted titles
+            aNewStringList = { aOldStringList[0] };
+            aNewStringList[0]->setString(aNewText);
+        }
     }
     else
     {
diff --git a/chart2/uiconfig/menubar/menubar.xml 
b/chart2/uiconfig/menubar/menubar.xml
index 58c84d6cbc33..e295d77a3943 100644
--- a/chart2/uiconfig/menubar/menubar.xml
+++ b/chart2/uiconfig/menubar/menubar.xml
@@ -120,6 +120,7 @@
         <menu:menuitem menu:id=".uno:View3D"/>
       <menu:menuseparator/>
       <menu:menuitem menu:id=".uno:FormatSelection" menu:style="text"/>
+      <menu:menuitem menu:id=".uno:FontDialog" menu:style="text"/>
       <menu:menuitem menu:id=".uno:TransformDialog" menu:style="text"/>
       <menu:menu menu:id=".uno:ArrangeRow">
         <menu:menupopup>
diff --git a/chart2/uiconfig/toolbar/toolbar.xml 
b/chart2/uiconfig/toolbar/toolbar.xml
index 809174ba206d..21105871c095 100644
--- a/chart2/uiconfig/toolbar/toolbar.xml
+++ b/chart2/uiconfig/toolbar/toolbar.xml
@@ -20,6 +20,7 @@
 <toolbar:toolbar xmlns:toolbar="http://openoffice.org/2001/toolbar"; 
xmlns:xlink="http://www.w3.org/1999/xlink";>
  <toolbar:toolbaritem xlink:href=".uno:ChartElementSelector"/>
  <toolbar:toolbaritem xlink:href=".uno:FormatSelection"/>
+ <toolbar:toolbaritem xlink:href=".uno:FontDialog"/>
  <toolbar:toolbarseparator/>
  <toolbar:toolbaritem xlink:href=".uno:DiagramType"/>
  <toolbar:toolbaritem xlink:href=".uno:DiagramArea"/>
commit a0d5fea9c3324af467b6f7728f1dfb7bfe4e5452
Author:     Noel Grandin <[email protected]>
AuthorDate: Wed Mar 6 09:43:28 2024 +0200
Commit:     Balazs Varga <[email protected]>
CommitDate: Wed Apr 3 10:18:25 2024 +0200

    tdf#158773 reduce cost of ContentInfo::GetText
    
    The specific path that is showing up on the perf profile is
    
    SdrTextObj::HasText -> EditTextObjectImpl::GetText ->
    ContentInfo::GetText
    
    Reduce the cost by 10% there by adding a method to check if we have text, 
and
    avoid the cost of constructing an OUString from an svl::SharedString.
    
    Also make use of the new method in places.
    
    Change-Id: Ibc2e0f61c4a2a6c33eea7f2cce09d692d82fd2b2
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164449
    Tested-by: Noel Grandin <[email protected]>
    Reviewed-by: Noel Grandin <[email protected]>

diff --git a/editeng/source/editeng/editobj.cxx 
b/editeng/source/editeng/editobj.cxx
index d0ec8632f134..a508e197f266 100644
--- a/editeng/source/editeng/editobj.cxx
+++ b/editeng/source/editeng/editobj.cxx
@@ -138,6 +138,12 @@ OUString ContentInfo::GetText() const
     return OUString(p);
 }
 
+sal_Int32 ContentInfo::GetTextLen() const
+{
+    const rtl_uString* p = maText.getData();
+    return p->length;
+}
+
 void ContentInfo::SetText( const OUString& rStr )
 {
     maText = svl::SharedString(rStr.pData, nullptr);
@@ -410,6 +416,14 @@ OUString EditTextObjectImpl::GetText(sal_Int32 nPara) const
     return maContents[nPara]->GetText();
 }
 
+sal_Int32 EditTextObjectImpl::GetTextLen(sal_Int32 nPara ) const
+{
+    if (nPara < 0 || o3tl::make_unsigned(nPara) >= maContents.size())
+        return 0;
+
+    return maContents[nPara]->GetTextLen();
+}
+
 void EditTextObjectImpl::ClearPortionInfo()
 {
     mpPortionInfo.reset();
diff --git a/editeng/source/editeng/editobj2.hxx 
b/editeng/source/editeng/editobj2.hxx
index 86c81e23f94e..81151448ee11 100644
--- a/editeng/source/editeng/editobj2.hxx
+++ b/editeng/source/editeng/editobj2.hxx
@@ -142,6 +142,7 @@ public:
     const svl::SharedString& GetSharedString() const { return maText;}
     OUString GetText() const;
     void SetText( const OUString& rStr );
+    sal_Int32 GetTextLen() const;
 
     void dumpAsXml(xmlTextWriterPtr pWriter) const;
 
@@ -228,6 +229,7 @@ public:
 
     virtual sal_Int32 GetParagraphCount() const override;
     virtual OUString GetText(sal_Int32 nParagraph) const override;
+    virtual sal_Int32 GetTextLen(sal_Int32 nParagraph) const override;
 
     virtual void ClearPortionInfo() override;
 
diff --git a/editeng/source/outliner/overflowingtxt.cxx 
b/editeng/source/outliner/overflowingtxt.cxx
index 42316fa1fa34..5943d7be6c54 100644
--- a/editeng/source/outliner/overflowingtxt.cxx
+++ b/editeng/source/outliner/overflowingtxt.cxx
@@ -45,7 +45,7 @@ std::optional<OutlinerParaObject> 
TextChainingUtils::JuxtaposeParaObject(
     // Special case: if only empty text remove it at the end
     bool bOnlyOneEmptyPara = !pNextPObj ||
                              (pOutl->GetParagraphCount() == 1 &&
-                              pNextPObj->GetTextObject().GetText(0).isEmpty());
+                              !pNextPObj->GetTextObject().HasText(0));
 
     EditEngine &rEditEngine = const_cast<EditEngine &>(pOutl->GetEditEngine());
 
diff --git a/include/editeng/editobj.hxx b/include/editeng/editobj.hxx
index 5badaf8e8a2a..b78b1a918b27 100644
--- a/include/editeng/editobj.hxx
+++ b/include/editeng/editobj.hxx
@@ -87,6 +87,10 @@ public:
 
     virtual OUString GetText(sal_Int32 nPara) const = 0;
 
+    virtual sal_Int32 GetTextLen(sal_Int32 nPara) const = 0;
+
+    bool HasText(sal_Int32 nPara) const { return GetTextLen(nPara) > 0; }
+
     virtual void ClearPortionInfo() = 0;
 
     virtual bool HasOnlineSpellErrors() const = 0;
diff --git a/sc/source/filter/xcl97/xcl97rec.cxx 
b/sc/source/filter/xcl97/xcl97rec.cxx
index f9031449bf70..3c054416d9b9 100644
--- a/sc/source/filter/xcl97/xcl97rec.cxx
+++ b/sc/source/filter/xcl97/xcl97rec.cxx
@@ -921,8 +921,7 @@ XclTxo::XclTxo( const XclExpRoot& rRoot, const 
EditTextObject& rEditObj, SdrObje
     // Excel has one alignment per NoteObject while Calc supports
     // one alignment per paragraph - use the first paragraph
     // alignment (if set) as our overall alignment.
-    OUString aParaText( rEditObj.GetText( 0 ) );
-    if( !aParaText.isEmpty() )
+    if( rEditObj.HasText( 0 ) )
     {
         const SfxItemSet& aSet( rEditObj.GetParaAttribs( 0));
         if( const SvxAdjustItem* pItem = aSet.GetItemIfSet( EE_PARA_JUST ) )
diff --git a/sc/source/ui/Accessibility/AccessiblePageHeader.cxx 
b/sc/source/ui/Accessibility/AccessiblePageHeader.cxx
index 075f72b0c349..2c72bf64318c 100644
--- a/sc/source/ui/Accessibility/AccessiblePageHeader.cxx
+++ b/sc/source/ui/Accessibility/AccessiblePageHeader.cxx
@@ -348,7 +348,7 @@ bool ScAccessiblePageHeader::IsDefunc( sal_Int64 
nParentStates )
 
 void ScAccessiblePageHeader::AddChild(const EditTextObject* pArea, sal_uInt32 
nIndex, SvxAdjust eAdjust)
 {
-    if (pArea && (!pArea->GetText(0).isEmpty() || (pArea->GetParagraphCount() 
> 1)))
+    if (pArea && ((pArea->GetParagraphCount() > 1) || pArea->HasText(0)))
     {
         if (maAreas[nIndex].is())
         {
diff --git a/sc/source/ui/view/viewfunc.cxx b/sc/source/ui/view/viewfunc.cxx
index a5c17784fbf9..38a0e02534ff 100644
--- a/sc/source/ui/view/viewfunc.cxx
+++ b/sc/source/ui/view/viewfunc.cxx
@@ -832,7 +832,7 @@ void ScViewFunc::EnterData( SCCOL nCol, SCROW nRow, SCTAB 
nTab,
             pDocSh->UpdateOle(GetViewData());
 
             bool bIsEmpty = rData.GetParagraphCount() == 0
-                || (rData.GetParagraphCount() == 1 && 
rData.GetText(0).isEmpty());
+                || (rData.GetParagraphCount() == 1 && !rData.HasText(0));
             const OUString aType(bIsEmpty ? u"delete-content" : 
u"cell-change");
             HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, rMark, 
nCol, nRow, aType);
 
diff --git a/sd/source/filter/ppt/pptinanimations.cxx 
b/sd/source/filter/ppt/pptinanimations.cxx
index 07a36e0eec98..9de191f2d068 100644
--- a/sd/source/filter/ppt/pptinanimations.cxx
+++ b/sd/source/filter/ppt/pptinanimations.cxx
@@ -2523,7 +2523,7 @@ void AnimationImporter::importTargetElementContainer( 
const Atom* pAtom, Any& rT
 
                     while( (nPara < nParaCount) && (begin > 0) )
                     {
-                        sal_Int32 nParaLength = rEditTextObject.GetText( nPara 
).getLength() + 1;
+                        sal_Int32 nParaLength = rEditTextObject.GetTextLen( 
nPara ) + 1;
                         begin -= nParaLength;
                         end -= nParaLength;
                         nPara++;
diff --git a/svx/source/svdraw/svdotxat.cxx b/svx/source/svdraw/svdotxat.cxx
index 35a467327dc9..1158deb8c160 100644
--- a/svx/source/svdraw/svdotxat.cxx
+++ b/svx/source/svdraw/svdotxat.cxx
@@ -424,17 +424,16 @@ bool SdrTextObj::HasText() const
 
     OutlinerParaObject* pOPO = GetOutlinerParaObject();
 
-    bool bHasText = false;
-    if( pOPO )
-    {
-        const EditTextObject& rETO = pOPO->GetTextObject();
-        sal_Int32 nParaCount = rETO.GetParagraphCount();
-
-        if( nParaCount > 0 )
-            bHasText = (nParaCount > 1) || (!rETO.GetText( 0 ).isEmpty());
-    }
+    if( !pOPO )
+        return false;
 
-    return bHasText;
+    const EditTextObject& rETO = pOPO->GetTextObject();
+    sal_Int32 nParaCount = rETO.GetParagraphCount();
+    if( nParaCount == 0 )
+        return false;
+    if( nParaCount > 1 )
+        return true;
+    return rETO.HasText( 0 );
 }
 
 void SdrTextObj::AppendFamilyToStyleName(OUString& styleName, SfxStyleFamily 
family)
diff --git a/svx/source/table/cell.cxx b/svx/source/table/cell.cxx
index bbef530c3dd1..74a8b822f539 100644
--- a/svx/source/table/cell.cxx
+++ b/svx/source/table/cell.cxx
@@ -562,7 +562,7 @@ bool Cell::hasText() const
         {
             if( rTextObj.GetParagraphCount() == 1 )
             {
-                if( rTextObj.GetText(0).isEmpty() )
+                if( !rTextObj.HasText(0) )
                     return false;
             }
             return true;
diff --git a/sw/source/uibase/docvw/AnnotationWin.cxx 
b/sw/source/uibase/docvw/AnnotationWin.cxx
index 3d350048055b..8bb2fcec68aa 100644
--- a/sw/source/uibase/docvw/AnnotationWin.cxx
+++ b/sw/source/uibase/docvw/AnnotationWin.cxx
@@ -441,7 +441,7 @@ void SwAnnotationWin::InitAnswer(OutlinerParaObject const & 
rText)
 
     // insert old, selected text or "..."
     // TODO: iterate over all paragraphs, not only first one to find out if it 
is empty
-    if (!rText.GetTextObject().GetText(0).isEmpty())
+    if (rText.GetTextObject().HasText(0))
         GetOutlinerView()->GetEditView().InsertText(rText.GetTextObject());
     else
         GetOutlinerView()->InsertText("...");
commit b3463ecc332145333b3411897e66111207322da6
Author:     Balazs Varga <[email protected]>
AuthorDate: Thu Mar 28 17:17:47 2024 +0100
Commit:     Balazs Varga <[email protected]>
CommitDate: Wed Apr 3 10:04:56 2024 +0200

    tdf#78027 - Fix Chart OOXML Import with non-uniform formatted titles
    
    Character formats are disappeared from Chart Title textobjects if they were
    formatted non-uniform. In this patch only the OOXML import and the chartview
    part will be fixed which make it visible after the import.
    
    (Note: next patch will contain the chart controller part where the 
characters
    can be formatted during title editing).
    
    cherry-picked from commit: f31a8ff9ea15ad81aeac265fce6eafe6342a68b7
    
    Change-Id: I4fb5c3d80b7889935d198e70fb49e2c68108b235
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/165500
    Tested-by: Jenkins
    Tested-by: Gabor Kelemen <[email protected]>
    Reviewed-by: Balazs Varga <[email protected]>

diff --git a/chart2/source/view/main/PropertyMapper.cxx 
b/chart2/source/view/main/PropertyMapper.cxx
index 2700769f595c..2b2934c47efd 100644
--- a/chart2/source/view/main/PropertyMapper.cxx
+++ b/chart2/source/view/main/PropertyMapper.cxx
@@ -268,7 +268,8 @@ const tPropertyNameMap& 
PropertyMapper::getPropertyNameMapForCharacterProperties
         {"CharColor",                "CharColor"},
         {"CharContoured",            "CharContoured"},
         {"CharEmphasis",             "CharEmphasis"},//the service 
style::CharacterProperties  describes a property called 'CharEmphasize' which 
is nowhere implemented
-
+        {"CharEscapement",           "CharEscapement"},
+        {"CharEscapementHeight",     "CharEscapementHeight"},
         {"CharFontFamily",           "CharFontFamily"},
         {"CharFontFamilyAsian",      "CharFontFamilyAsian"},
         {"CharFontFamilyComplex",    "CharFontFamilyComplex"},
diff --git a/chart2/source/view/main/ShapeFactory.cxx 
b/chart2/source/view/main/ShapeFactory.cxx
index f9efceb676a5..1384372cda33 100644
--- a/chart2/source/view/main/ShapeFactory.cxx
+++ b/chart2/source/view/main/ShapeFactory.cxx
@@ -2177,7 +2177,8 @@ rtl::Reference<SvxShapeText>
 
         //set text and text properties
         uno::Reference< text::XTextCursor > xTextCursor( 
xShape->createTextCursor() );
-        if( !xTextCursor.is() )
+        uno::Reference< text::XTextCursor > xSelectionCursor( 
xShape->createTextCursor() );
+        if( !xTextCursor.is() || !xSelectionCursor.is() )
             return xShape;
 
         tPropertyNameValueMap aValueMap;
@@ -2226,18 +2227,32 @@ rtl::Reference<SvxShapeText>
             //if the characters should be stacked we use only the first 
character properties for code simplicity
             if( xFormattedString.hasElements() )
             {
-                OUString aLabel;
-                for( const auto & i : std::as_const(xFormattedString) )
-                    aLabel += i->getString();
-                aLabel = ShapeFactory::getStackedString( aLabel, 
bStackCharacters );
-
-                xTextCursor->gotoEnd(false);
-                xShape->insertString( xTextCursor, aLabel, false );
-                xTextCursor->gotoEnd(true);
-                uno::Reference< beans::XPropertySet > xSourceProps( 
xFormattedString[0], uno::UNO_QUERY );
-
-                PropertyMapper::setMappedProperties( *xShape, xSourceProps
-                        , 
PropertyMapper::getPropertyNameMapForCharacterProperties() );
+                size_t nLBreaks = xFormattedString.size() - 1;
+                uno::Reference< beans::XPropertySet > 
xSelectionProp(xSelectionCursor, uno::UNO_QUERY);
+                for (const uno::Reference<chart2::XFormattedString>& rxFS : 
xFormattedString)
+                {
+                    if (!rxFS->getString().isEmpty())
+                    {
+                        xTextCursor->gotoEnd(false);
+                        xSelectionCursor->gotoEnd(false);
+                        OUString aLabel = 
ShapeFactory::getStackedString(rxFS->getString(), bStackCharacters);
+                        if (nLBreaks-- > 0)
+                            aLabel += OUStringChar(' ');
+                        xShape->insertString(xTextCursor, aLabel, false);
+                        xSelectionCursor->gotoEnd(true); // select current 
paragraph
+                        uno::Reference< beans::XPropertySet > 
xSourceProps(rxFS, uno::UNO_QUERY);
+                        if (xFormattedString.size() > 1 && xSelectionProp.is())
+                        {
+                            
PropertyMapper::setMappedProperties(xSelectionProp, xSourceProps,
+                                
PropertyMapper::getPropertyNameMapForTextShapeProperties());
+                        }
+                        else
+                        {
+                            PropertyMapper::setMappedProperties(*xShape, 
xSourceProps,
+                                
PropertyMapper::getPropertyNameMapForTextShapeProperties());
+                        }
+                    }
+                }
 
                 // adapt font size according to page size
                 awt::Size aOldRefSize;
@@ -2249,23 +2264,34 @@ rtl::Reference<SvxShapeText>
         }
         else
         {
-            for( const uno::Reference< chart2::XFormattedString >& rxFS : 
std::as_const(xFormattedString) )
+            uno::Reference< beans::XPropertySet > 
xSelectionProp(xSelectionCursor, uno::UNO_QUERY);
+            for (const uno::Reference<chart2::XFormattedString>& rxFS : 
xFormattedString)
             {
-                xTextCursor->gotoEnd(false);
-                xShape->insertString( xTextCursor, rxFS->getString(), false );
-                xTextCursor->gotoEnd(true);
+                if (!rxFS->getString().isEmpty())
+                {
+                    xTextCursor->gotoEnd(false);
+                    xSelectionCursor->gotoEnd(false);
+                    xShape->insertString(xTextCursor, rxFS->getString(), 
false);
+                    xSelectionCursor->gotoEnd(true); // select current 
paragraph
+                    uno::Reference< beans::XPropertySet > xSourceProps(rxFS, 
uno::UNO_QUERY);
+                    if (xFormattedString.size() > 1 && xSelectionProp.is())
+                    {
+                        PropertyMapper::setMappedProperties(xSelectionProp, 
xSourceProps,
+                            
PropertyMapper::getPropertyNameMapForTextShapeProperties());
+                    }
+                    else
+                    {
+                        PropertyMapper::setMappedProperties(*xShape, 
xSourceProps,
+                            
PropertyMapper::getPropertyNameMapForTextShapeProperties());
+                    }
+                }
             }
-            awt::Size aOldRefSize;
-            bool bHasRefPageSize =
-                ( xTextProperties->getPropertyValue( "ReferencePageSize") >>= 
aOldRefSize );
 
             if( xFormattedString.hasElements() )
             {
-                uno::Reference< beans::XPropertySet > xSourceProps( 
xFormattedString[0], uno::UNO_QUERY );
-                PropertyMapper::setMappedProperties( *xShape, xSourceProps, 
PropertyMapper::getPropertyNameMapForCharacterProperties() );
-
                 // adapt font size according to page size
-                if( bHasRefPageSize )
+                awt::Size aOldRefSize;
+                if( xTextProperties->getPropertyValue("ReferencePageSize") >>= 
aOldRefSize )
                 {
                     RelativeSizeHelper::adaptFontSizes( *xShape, aOldRefSize, 
rSize );
                 }
diff --git a/oox/inc/drawingml/textcharacterproperties.hxx 
b/oox/inc/drawingml/textcharacterproperties.hxx
index 40842585d10a..9750407b817c 100644
--- a/oox/inc/drawingml/textcharacterproperties.hxx
+++ b/oox/inc/drawingml/textcharacterproperties.hxx
@@ -64,6 +64,12 @@ struct TextCharacterProperties
     /// Set if there was a property set that alters run visually during import
     bool mbHasVisualRunProperties;
 
+    /// Set if there was an empty paragraph property set during import
+    /// <a:pPr><a:defRPr/></a:pPr>
+    /// In that case we use the default paragraph properties from the
+    /// <c:txPr><a:p><a:pPr><a:defRPr>...</a:defRPr>
+    bool mbHasEmptyParaProperties;
+
     std::vector<css::beans::PropertyValue> maTextEffectsProperties;
 
     /** Overwrites all members that are explicitly set in rSourceProps. */
@@ -84,7 +90,7 @@ struct TextCharacterProperties
                             PropertySet& rPropSet,
                             const ::oox::core::XmlFilterBase& rFilter ) const;
 
-    TextCharacterProperties() : mbHasVisualRunProperties(false) {}
+    TextCharacterProperties() : mbHasVisualRunProperties(false), 
mbHasEmptyParaProperties(false) {}
 };
 
 
diff --git a/oox/source/drawingml/chart/titleconverter.cxx 
b/oox/source/drawingml/chart/titleconverter.cxx
index 91684393c627..e09108f6a171 100644
--- a/oox/source/drawingml/chart/titleconverter.cxx
+++ b/oox/source/drawingml/chart/titleconverter.cxx
@@ -90,7 +90,16 @@ Sequence< Reference< XFormattedString > > 
TextConverter::createStringSequence(
                 bool bAddNewLine = ((aRIt + 1 == aREnd) && (aPIt + 1 != 
aPEnd)) || rTextRun.isLineBreak();
                 Reference< XFormattedString > xFmtStr = appendFormattedString( 
aStringVec, rTextRun.getText(), bAddNewLine );
                 PropertySet aPropSet( xFmtStr );
-                TextCharacterProperties aRunProps( rParaProps );
+                TextCharacterProperties aRunProps;
+                if (rParaProps.mbHasEmptyParaProperties && rxTextProp.is() && 
rxTextProp->hasParagraphProperties())
+                {
+                    const TextParagraphVector rDefTextParas = 
rxTextProp->getParagraphs();
+                    TextParagraphVector::const_iterator aDefPIt = 
rDefTextParas.begin();
+                    const TextParagraph& rDefTextPara = **aDefPIt;
+                    aRunProps = 
rDefTextPara.getProperties().getTextCharacterProperties();
+                }
+                else
+                    aRunProps = rParaProps;
                 aRunProps.assignUsed( rTextRun.getTextCharacterProperties() );
                 getFormatter().convertTextFormatting( aPropSet, aRunProps, 
eObjType );
             }
diff --git a/oox/source/drawingml/textcharacterpropertiescontext.cxx 
b/oox/source/drawingml/textcharacterpropertiescontext.cxx
index fb4390e4c7a7..4935e8a75a68 100644
--- a/oox/source/drawingml/textcharacterpropertiescontext.cxx
+++ b/oox/source/drawingml/textcharacterpropertiescontext.cxx
@@ -50,6 +50,9 @@ 
TextCharacterPropertiesContext::TextCharacterPropertiesContext(
     int nVisualTokenAmount = sax_fastparser::castToFastAttributeList(
                 rAttribs.getFastAttributeList() 
).getFastAttributeTokens().size();
 
+    if (nVisualTokenAmount == 0)
+        mrTextCharacterProperties.mbHasEmptyParaProperties = true;
+
     if ( rAttribs.hasAttribute( XML_lang ) )
     {
         mrTextCharacterProperties.moLang = rAttribs.getString( XML_lang );

Reply via email to