tdf#79542 xls: applyGroupBox to radiobutton groups

A group box control links radiobuttons together,
and so does a sheet.

No matching unit tests were found.

make CppunitTest_sc_subsequent_filters_test4 \
    CPPUNIT_TEST_NAME=testLegacyOptionButtonGroupBox

Change-Id: Ib5b03c68b5218649268f283d11981cc03fe4850a
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137838
Tested-by: Jenkins
Reviewed-by: Justin Luth <jluth@mail.com>
diff --git a/sc/qa/unit/data/xls/tdf79542_radioGroupBox.xls b/sc/qa/unit/data/xls/tdf79542_radioGroupBox.xls
new file mode 100644
index 0000000..1861913
--- /dev/null
+++ b/sc/qa/unit/data/xls/tdf79542_radioGroupBox.xls
Binary files differ
diff --git a/sc/qa/unit/subsequent_filters_test4.cxx b/sc/qa/unit/subsequent_filters_test4.cxx
index fcbc02e..d6b8536 100644
--- a/sc/qa/unit/subsequent_filters_test4.cxx
+++ b/sc/qa/unit/subsequent_filters_test4.cxx
@@ -116,6 +116,26 @@ CPPUNIT_TEST_FIXTURE(ScFiltersTest4, testControlImport)
                                                         UNO_QUERY_THROW);
}

CPPUNIT_TEST_FIXTURE(ScFiltersTest4, testLegacyOptionButtonGroupBox)
{
    createScDoc("xls/tdf79542_radioGroupBox.xls");
    uno::Reference<sheet::XSpreadsheetDocument> xDoc(mxComponent, UNO_QUERY_THROW);
    uno::Reference<container::XIndexAccess> xIA(xDoc->getSheets(), UNO_QUERY_THROW);
    uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xIA->getByIndex(0),
                                                                 UNO_QUERY_THROW);
    uno::Reference<container::XIndexAccess> xIA_DrawPage(xDrawPageSupplier->getDrawPage(),
                                                         UNO_QUERY_THROW);

    OUString sGroupName;
    uno::Reference<drawing::XControlShape> xControlShape(xIA_DrawPage->getByIndex(1),
                                                         UNO_QUERY_THROW);
    uno::Reference<beans::XPropertySet> xPropertySet(xControlShape->getControl(),
                                                     uno::UNO_QUERY_THROW);
    // The radio buttons are grouped by GroupBoxes - so the name comes from the group shape name
    xPropertySet->getPropertyValue("GroupName") >>= sGroupName;
    CPPUNIT_ASSERT_EQUAL(OUString("Casella di gruppo 1"), sGroupName);
}

CPPUNIT_TEST_FIXTURE(ScFiltersTest4, testActiveXOptionButtonGroup)
{
    createScDoc("xlsx/tdf111980_radioButtons.xlsx");
diff --git a/sc/source/filter/excel/xiescher.cxx b/sc/source/filter/excel/xiescher.cxx
index e9a47fa..1805b7a 100644
--- a/sc/source/filter/excel/xiescher.cxx
+++ b/sc/source/filter/excel/xiescher.cxx
@@ -378,6 +378,11 @@ void XclImpDrawObjBase::SetAnchor( const XclObjAnchor& rAnchor )
    mbHasAnchor = true;
}

const tools::Rectangle& XclImpDrawObjBase::GetDffRect() const
{
    return maDffRect;
}

void XclImpDrawObjBase::SetDffData(
    const DffObjData& rDffObjData, const OUString& rObjName, const OUString& rHyperlink,
    bool bVisible, bool bAutoMargin )
@@ -388,6 +393,7 @@ void XclImpDrawObjBase::SetDffData(
    maHyperlink = rHyperlink;
    mbVisible = bVisible;
    mbAutoMargin = bAutoMargin;
    maDffRect = rDffObjData.aChildAnchor;
}

OUString XclImpDrawObjBase::GetObjName() const
@@ -2086,6 +2092,16 @@ void XclImpTbxObjBase::SetDffProperties( const DffPropSet& rDffPropSet )
    ::set_flag( maLineData.mnAuto, EXC_OBJ_FILL_AUTO, false );
}

void XclImpControlHelper::SetStringProperty(const OUString& sName, const OUString& sVal)
{
    Reference<XControlModel> xCtrlModel = XclControlHelper::GetControlModel(mxShape);
    if (!xCtrlModel.is())
        return;

    ScfPropertySet aProps(xCtrlModel);
    aProps.SetStringProperty(sName, sVal);
}

bool XclImpTbxObjBase::FillMacroDescriptor( ScriptEventDescriptor& rDescriptor ) const
{
    return XclControlHelper::FillMacroDescriptor( rDescriptor, DoGetEventType(), GetMacroName(), GetDocShell() );
@@ -2393,6 +2409,11 @@ XclTbxEventType XclImpOptionButtonObj::DoGetEventType() const
    return EXC_TBX_EVENT_ACTION;
}

bool XclImpOptionButtonObj::IsInGroup() const
{
    return mnNextInGroup;
}

XclImpLabelObj::XclImpLabelObj( const XclImpRoot& rRoot ) :
    XclImpTbxObjBase( rRoot )
{
@@ -4070,6 +4091,43 @@ const XclImpObjTextData* XclImpDrawing::FindTextData( const DffRecordHeader& rHe
    return nullptr;
}

void XclImpDrawing::ApplyGroupBoxes()
{
    // sorted: smallest to largest - looking for smallest contained-in GroupBox
    // multimap: allows duplicate key values - may have identical areas.
    std::multimap<double, XclImpDrawObjRef> aGroupBoxAreaMap;
    for (auto& rGroupBox : maObjMapId)
    {
        if (rGroupBox.second->GetObjType() != EXC_OBJTYPE_GROUPBOX)
            continue;
        const tools::Rectangle& rRect = rGroupBox.second->GetDffRect();
        const double fArea = double(rRect.GetWidth()) * rRect.GetHeight();
        aGroupBoxAreaMap.insert(std::pair<double, XclImpDrawObjRef>(fArea, rGroupBox.second));
    }

    for (auto& rGroupedObj : maObjMapId)
    {
        auto pRadioButton = dynamic_cast<XclImpOptionButtonObj*>(rGroupedObj.second.get());
        if (!pRadioButton || pRadioButton->IsInGroup())
            continue;

        OUString sGroupName("autoGroup_");
        for (auto& rGroupBox : aGroupBoxAreaMap)
        {
            assert(pRadioButton->GetTab() == rGroupBox.second->GetTab() && "impossible right?");
            if (!rGroupBox.second->GetDffRect().Contains(pRadioButton->GetDffRect()))
                continue;

            sGroupName = rGroupBox.second->GetObjName();
            if (sGroupName.isEmpty())
                sGroupName += "autoGroup_" + OUString::number(rGroupBox.second->GetObjId());
            // I ASSUME the smallest box wins in MS Word. (otherwise first? last?)
            break;
        }
        pRadioButton->SetStringProperty("GroupName", sGroupName);
    }
}

void XclImpDrawing::SetSkipObj( sal_uInt16 nObjId )
{
    maSkipObjs.push_back( nObjId );
@@ -4097,6 +4155,8 @@ void XclImpDrawing::ImplConvertObjects( XclImpDffConverter& rDffConv, SdrModel& 
    rDffConv.ProcessDrawing( maRawObjs );
    // process all objects in the DFF stream
    rDffConv.ProcessDrawing( maDffStrm );
    // assign groups based on being contained in the same GroupBox/sheet
    ApplyGroupBoxes();
    // unregister this drawing manager at the passed (global) DFF manager
    rDffConv.FinalizeDrawing();
    rSdrModel.EnableUndo(bOrigUndoStatus);
diff --git a/sc/source/filter/inc/xiescher.hxx b/sc/source/filter/inc/xiescher.hxx
index 2079d68..cf09c16 100644
--- a/sc/source/filter/inc/xiescher.hxx
+++ b/sc/source/filter/inc/xiescher.hxx
@@ -98,6 +98,7 @@ public:
    sal_uInt32   GetDffShapeId() const { return mnDffShapeId; }
    /** Returns the shape flags from the DFF stream. */
    ShapeFlag    GetDffFlags() const { return mnDffFlags; }
    const tools::Rectangle& GetDffRect() const;

    /** Returns true, if the object is hidden. */
    bool         IsHidden() const { return mbHidden; }
@@ -191,6 +192,7 @@ private:
    sal_uInt16          mnObjType;      /// The Excel object type from OBJ record.
    sal_uInt32          mnDffShapeId;   /// Shape ID from DFF stream.
    ShapeFlag           mnDffFlags;     /// Shape flags from DFF stream.
    tools::Rectangle maDffRect;
    OUString       maObjName;      /// Name of the object.
    OUString       maMacroName;    /// Name of an attached macro.
    OUString       maHyperlink;    /// On-click hyperlink URL.
@@ -470,6 +472,7 @@ public:

    /** Sets additional properties to the form control model, calls virtual DoProcessControl(). */
    void                ProcessControl( const XclImpDrawObjBase& rDrawObj ) const;
    void SetStringProperty(const OUString& sName, const OUString& sVal);

protected:
    /** Reads the formula for the linked cell from the current position of the stream. */
@@ -571,6 +574,7 @@ class XclImpOptionButtonObj final : public XclImpCheckBoxObj
{
public:
    explicit            XclImpOptionButtonObj( const XclImpRoot& rRoot );
    bool IsInGroup() const;

private:
    /** Reads the contents of the a BIFF5 OBJ record from the passed stream. */
@@ -1062,6 +1066,8 @@ public:
    /** Finds the textbox data related to the DFF shape at the passed position. */
    const XclImpObjTextData* FindTextData( const DffRecordHeader& rHeader ) const;

    void ApplyGroupBoxes();

    /** Sets the object with the passed identification to be skipped on import. */
    void                SetSkipObj( sal_uInt16 nObjId );
    /** Returns the size of the progress bar shown while processing all objects. */