1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <drawingfragment.hxx>
22 #include <basegfx/matrix/b2dhommatrix.hxx>
23 #include <comphelper/propertyvalue.hxx>
25 #include <com/sun/star/beans/XPropertySet.hpp>
26 #include <com/sun/star/container/XIndexContainer.hpp>
27 #include <com/sun/star/container/XNameReplace.hpp>
28 #include <com/sun/star/document/XEventsSupplier.hpp>
29 #include <com/sun/star/drawing/XControlShape.hpp>
30 #include <com/sun/star/drawing/XDrawPage.hpp>
31 #include <com/sun/star/drawing/XShapes.hpp>
32 #include <com/sun/star/script/ScriptEventDescriptor.hpp>
33 #include <com/sun/star/script/XEventAttacherManager.hpp>
34 #include <rtl/strbuf.hxx>
35 #include <svx/svdobj.hxx>
36 #include <drwlayer.hxx>
37 #include <oox/core/filterbase.hxx>
38 #include <oox/drawingml/connectorshapecontext.hxx>
39 #include <oox/drawingml/graphicshapecontext.hxx>
40 #include <oox/helper/attributelist.hxx>
41 #include <oox/helper/propertyset.hxx>
42 #include <oox/shape/ShapeDrawingFragmentHandler.hxx>
43 #include <oox/token/namespaces.hxx>
44 #include <oox/token/properties.hxx>
45 #include <oox/token/tokens.hxx>
46 #include <oox/vml/vmlshape.hxx>
47 #include <oox/vml/vmlshapecontainer.hxx>
48 #include <osl/diagnose.h>
49 #include <o3tl/string_view.hxx>
50 #include <formulaparser.hxx>
51 #include <stylesbuffer.hxx>
52 #include <themebuffer.hxx>
53 #include <worksheetbuffer.hxx>
57 using namespace ::com::sun::star::beans
;
58 using namespace ::com::sun::star::container
;
59 using namespace ::com::sun::star::document
;
60 using namespace ::com::sun::star::drawing
;
61 using namespace ::com::sun::star::script
;
62 using namespace ::com::sun::star::uno
;
63 using namespace ::oox::core
;
64 using namespace ::oox::drawingml
;
65 using namespace ::oox::ole
;
67 using ::com::sun::star::awt::Size
;
68 using ::com::sun::star::awt::Point
;
69 using ::com::sun::star::awt::Rectangle
;
70 using ::com::sun::star::awt::XControlModel
;
71 // no using's for ::oox::vml, that may clash with ::oox::drawingml types
73 ShapeMacroAttacher::ShapeMacroAttacher( const OUString
& rMacroName
, const Reference
< XShape
>& rxShape
) :
74 VbaMacroAttacherBase( rMacroName
),
79 void ShapeMacroAttacher::attachMacro( const OUString
& rMacroUrl
)
83 Reference
< XEventsSupplier
> xSupplier( mxShape
, UNO_QUERY_THROW
);
84 Reference
< XNameReplace
> xEvents( xSupplier
->getEvents(), UNO_SET_THROW
);
85 Sequence aEventProps
{ comphelper::makePropertyValue("EventType", OUString( "Script" )),
86 comphelper::makePropertyValue("Script", rMacroUrl
) };
87 xEvents
->replaceByName( "OnClick", Any( aEventProps
) );
94 Shape::Shape( const WorksheetHelper
& rHelper
, const AttributeList
& rAttribs
, const char* pcServiceName
) :
95 ::oox::drawingml::Shape( pcServiceName
),
96 WorksheetHelper( rHelper
)
98 OUString aMacro
= rAttribs
.getXString( XML_macro
, OUString() );
99 if( !aMacro
.isEmpty() )
100 maMacroName
= getFormulaParser().importMacroName( aMacro
);
103 void Shape::finalizeXShape( XmlFilterBase
& rFilter
, const Reference
< XShapes
>& rxShapes
)
106 getShapeProperties().getProperty( PROP_URL
) >>= sURL
;
107 getWorksheets().convertSheetNameRef( sURL
);
108 if( !maMacroName
.isEmpty() && mxShape
.is() )
110 VbaMacroAttacherRef xAttacher
= std::make_shared
<ShapeMacroAttacher
>( maMacroName
, mxShape
);
111 getBaseFilter().getVbaProject().registerMacroAttacher( xAttacher
);
113 ::oox::drawingml::Shape::finalizeXShape( rFilter
, rxShapes
);
114 if ( !sURL
.isEmpty() )
116 SdrObject
* pObj
= SdrObject::getSdrObjectFromXShape( mxShape
);
118 pObj
->setHyperlink(sURL
);
122 GroupShapeContext::GroupShapeContext( const FragmentHandler2
& rParent
,
123 const WorksheetHelper
& rHelper
, const ShapePtr
& rxParentShape
, const ShapePtr
& rxShape
) :
124 ShapeGroupContext( rParent
, rxParentShape
, rxShape
),
125 WorksheetHelper( rHelper
)
129 /*static*/ ContextHandlerRef
GroupShapeContext::createShapeContext( FragmentHandler2
& rParent
,
130 const WorksheetHelper
& rHelper
, sal_Int32 nElement
, const AttributeList
& rAttribs
,
131 const ShapePtr
& rxParentShape
, ShapePtr
* pxShape
)
135 case XDR_TOKEN( sp
):
137 ShapePtr xShape
= std::make_shared
<Shape
>( rHelper
, rAttribs
, "com.sun.star.drawing.CustomShape" );
138 if( pxShape
) *pxShape
= xShape
;
139 return new ShapeContext( rParent
, rxParentShape
, xShape
);
141 case XDR_TOKEN( cxnSp
):
143 ShapePtr xShape
= std::make_shared
<Shape
>( rHelper
, rAttribs
, "com.sun.star.drawing.ConnectorShape" );
144 if( pxShape
) *pxShape
= xShape
;
145 return new ConnectorShapeContext(rParent
, rxParentShape
, xShape
,
146 xShape
->getConnectorShapeProperties());
148 case XDR_TOKEN( pic
):
150 ShapePtr xShape
= std::make_shared
<Shape
>( rHelper
, rAttribs
, "com.sun.star.drawing.GraphicObjectShape" );
151 if( pxShape
) *pxShape
= xShape
;
152 return new GraphicShapeContext( rParent
, rxParentShape
, xShape
);
154 case XDR_TOKEN( graphicFrame
):
156 ShapePtr xShape
= std::make_shared
<Shape
>( rHelper
, rAttribs
, "com.sun.star.drawing.GraphicObjectShape" );
157 if( pxShape
) *pxShape
= xShape
;
158 return new GraphicalObjectFrameContext( rParent
, rxParentShape
, xShape
, rHelper
.getSheetType() != WorksheetType::Chart
);
160 case XDR_TOKEN( grpSp
):
162 ShapePtr xShape
= std::make_shared
<Shape
>( rHelper
, rAttribs
, "com.sun.star.drawing.GroupShape" );
163 if( pxShape
) *pxShape
= xShape
;
164 return new GroupShapeContext( rParent
, rHelper
, rxParentShape
, xShape
);
170 ContextHandlerRef
GroupShapeContext::onCreateContext(
171 sal_Int32 nElement
, const AttributeList
& rAttribs
)
173 ContextHandlerRef xContext
= createShapeContext( *this, *this, nElement
, rAttribs
, mpGroupShapePtr
);
174 return xContext
? xContext
: ShapeGroupContext::onCreateContext( nElement
, rAttribs
);
177 DrawingFragment::DrawingFragment( const WorksheetHelper
& rHelper
, const OUString
& rFragmentPath
) :
178 WorksheetFragmentBase( rHelper
, rFragmentPath
),
179 mxDrawPage( rHelper
.getDrawPage() )
181 OSL_ENSURE( mxDrawPage
.is(), "DrawingFragment::DrawingFragment - missing drawing page" );
184 ContextHandlerRef
DrawingFragment::onCreateContext( sal_Int32 nElement
, const AttributeList
& rAttribs
)
186 switch( getCurrentElement() )
188 case XML_ROOT_CONTEXT
:
189 if( nElement
== XDR_TOKEN( wsDr
) ) return this;
192 case XDR_TOKEN( wsDr
):
195 case XDR_TOKEN( absoluteAnchor
):
196 case XDR_TOKEN( oneCellAnchor
):
197 case XDR_TOKEN( twoCellAnchor
):
198 mxAnchor
.reset( new ShapeAnchor( *this ) );
199 mxAnchor
->importAnchor( nElement
, rAttribs
);
204 case XDR_TOKEN( absoluteAnchor
):
205 case XDR_TOKEN( oneCellAnchor
):
206 case XDR_TOKEN( twoCellAnchor
):
210 case XDR_TOKEN( from
):
211 case XDR_TOKEN( to
): return this;
213 case XDR_TOKEN( pos
): if( mxAnchor
) mxAnchor
->importPos( rAttribs
); break;
214 case XDR_TOKEN( ext
): if( mxAnchor
) mxAnchor
->importExt( rAttribs
); break;
215 case XDR_TOKEN( clientData
): if( mxAnchor
) mxAnchor
->importClientData( rAttribs
); break;
217 default: return GroupShapeContext::createShapeContext( *this, *this, nElement
, rAttribs
, ShapePtr(), &mxShape
);
222 case XDR_TOKEN( from
):
223 case XDR_TOKEN( to
):
226 case XDR_TOKEN( col
):
227 case XDR_TOKEN( row
):
228 case XDR_TOKEN( colOff
):
229 case XDR_TOKEN( rowOff
): return this; // collect index in onCharacters()
236 void DrawingFragment::onCharacters( const OUString
& rChars
)
238 switch( getCurrentElement() )
240 case XDR_TOKEN( col
):
241 case XDR_TOKEN( row
):
242 case XDR_TOKEN( colOff
):
243 case XDR_TOKEN( rowOff
):
244 if( mxAnchor
) mxAnchor
->setCellPos( getCurrentElement(), getParentElement(), rChars
);
249 void DrawingFragment::onEndElement()
251 switch( getCurrentElement() )
253 case XDR_TOKEN( absoluteAnchor
):
254 case XDR_TOKEN( oneCellAnchor
):
255 case XDR_TOKEN( twoCellAnchor
):
256 if( mxDrawPage
.is() && mxShape
&& mxAnchor
)
258 EmuRectangle aShapeRectEmu
= mxAnchor
->calcAnchorRectEmu( getDrawPageSize() );
259 const bool bIsShapeVisible
= mxAnchor
->isAnchorValid();
260 if( (aShapeRectEmu
.X
>= 0) && (aShapeRectEmu
.Y
>= 0) && (aShapeRectEmu
.Width
>= 0) && (aShapeRectEmu
.Height
>= 0) )
262 const sal_Int32 aRotation
= mxShape
->getRotation();
263 if ((aRotation
>= 45 * PER_DEGREE
&& aRotation
< 135 * PER_DEGREE
)
264 || (aRotation
>= 225 * PER_DEGREE
&& aRotation
< 315 * PER_DEGREE
))
266 // When rotating any shape in MSO Excel within the range of degrees given above,
267 // Excel changes the cells in which the shape is anchored. The new position of
268 // the anchors are always calculated using a 90 degrees rotation anticlockwise.
269 // There is an important result of this operation: the top left point of the shape changes,
270 // it will be another vertex.
271 // The anchor position is given in the xml file, it is in the xdr:from and xdr:to elements.
272 // Let's see what happens in time order:
273 // We create a shape in Excel, the anchor position is in a given cell, then the rotation happens
274 // as mentioned above, and excel recalculates the cells in which the anchors are positioned.
275 // This new cell is exported into the xml elements xdr:from and xdr:to, when Excel exports the document!
276 // Thus, if we have a 90 degrees rotation and an already rotated point from which we base
277 // our calculations here in LO, the result is an incorrect 180 degrees rotation.
278 // Now, we need to create the bounding rectangle of the shape with this in mind.
279 // (Important to mention that at this point we don't talk about rotations at all, this bounding
280 // rectangle contains the original not-rotated shape. Rotation happens later in the code.)
281 // We get the new (x, y) coords, then swap width with height.
282 // To get the new coords we reflect the rectangle in the line y = x. (This will return the
283 // correct vertex, which is the actual top left one.)
284 // Another fact that appears to be true in Excel is that there are only 2 of possible anchor
285 // positions for a shape that is only rotated (and not resized for example).
286 // The first position happens in the set of degrees {[45, 135) U [225, 315)} and the second
287 // set is all the other angles. The two sets partition the circle (of all rotations: 360 degrees).
288 sal_Int64 nHalfWidth
= aShapeRectEmu
.Width
/ 2;
289 sal_Int64 nHalfHeight
= aShapeRectEmu
.Height
/ 2;
290 aShapeRectEmu
.X
= aShapeRectEmu
.X
+ nHalfWidth
- nHalfHeight
;
291 aShapeRectEmu
.Y
= aShapeRectEmu
.Y
+ nHalfHeight
- nHalfWidth
;
292 std::swap(aShapeRectEmu
.Width
, aShapeRectEmu
.Height
);
295 // TODO: DrawingML implementation expects 32-bit coordinates for EMU rectangles (change that to EmuRectangle)
296 // tdf#135918: Negative X,Y position has to be allowed to avoid shape displacement on rotation.
297 // The negative values can exist because of previous lines where the anchor rectangle must be mirrored in some ranges.
298 Rectangle
aShapeRectEmu32(
299 getLimitedValue
< sal_Int32
, sal_Int64
>( aShapeRectEmu
.X
, SAL_MIN_INT32
, SAL_MAX_INT32
),
300 getLimitedValue
< sal_Int32
, sal_Int64
>( aShapeRectEmu
.Y
, SAL_MIN_INT32
, SAL_MAX_INT32
),
301 getLimitedValue
< sal_Int32
, sal_Int64
>( aShapeRectEmu
.Width
, 0, SAL_MAX_INT32
),
302 getLimitedValue
< sal_Int32
, sal_Int64
>( aShapeRectEmu
.Height
, 0, SAL_MAX_INT32
) );
304 // Make sure to set the position and size *before* calling addShape().
305 mxShape
->setPosition(Point(aShapeRectEmu32
.X
, aShapeRectEmu32
.Y
));
306 mxShape
->setSize(Size(aShapeRectEmu32
.Width
, aShapeRectEmu32
.Height
));
308 // tdf#83671. Because Excel saves a diagram with zero size in xdr:xfm, the
309 // initial diagram import produces a background shape with zero size and no
310 // diagram shapes at all. Here the size has been determined from the anchor and
311 // thus repeating the import of diagram.xml gives the diagram shapes.
312 if (mxShape
->getDiagramDoms().getLength() > 0
313 && mxShape
->getChildren().size() == 1
314 && mxShape
->getExtDrawings().size() == 1)
316 mxShape
->getChildren()[0]->setSize(mxShape
->getSize());
317 OUString
sFragmentPath(
318 getFragmentPathFromRelId(mxShape
->getExtDrawings()[0]));
319 // Don't know why importFragment looses shape name and id. Rescue them.
320 OUString
sBackupName(mxShape
->getName());
321 OUString
sBackupId(mxShape
->getId());
322 getOoxFilter().importFragment(new oox::shape::ShapeDrawingFragmentHandler(
323 getOoxFilter(), sFragmentPath
, mxShape
));
324 mxShape
->setName(sBackupName
);
325 mxShape
->setId(sBackupId
);
328 if (mxShape
->getFontRefColorForNodes().isUsed())
329 applyFontRefColor(mxShape
, mxShape
->getFontRefColorForNodes());
331 basegfx::B2DHomMatrix aTransformation
;
332 if ( !bIsShapeVisible
)
333 mxShape
->setHidden(true);
335 mxShape
->addShape( getOoxFilter(), &getTheme(), mxDrawPage
, aTransformation
, mxShape
->getFillProperties() );
337 /* Collect all shape positions in the WorksheetHelper base
338 class. But first, scale EMUs to 1/100 mm. */
339 Rectangle
aShapeRectHmm(
340 convertEmuToHmm(aShapeRectEmu32
.X
> 0 ? aShapeRectEmu32
.X
: 0), convertEmuToHmm(aShapeRectEmu32
.Y
> 0 ? aShapeRectEmu32
.Y
: 0),
341 convertEmuToHmm(aShapeRectEmu32
.Width
), convertEmuToHmm(aShapeRectEmu32
.Height
) );
342 extendShapeBoundingBox( aShapeRectHmm
);
343 // set cell Anchoring
344 if ( mxAnchor
->getEditAs() != ShapeAnchor::ANCHOR_ABSOLUTE
)
346 SdrObject
* pObj
= SdrObject::getSdrObjectFromXShape( mxShape
->getXShape() );
349 bool bResizeWithCell
= mxAnchor
->getEditAs() == ShapeAnchor::ANCHOR_TWOCELL
;
350 ScDrawLayer::SetCellAnchoredFromPosition( *pObj
, getScDocument(), getSheetIndex(), bResizeWithCell
);
361 void DrawingFragment::applyFontRefColor(const oox::drawingml::ShapePtr
& pShape
,
362 const oox::drawingml::Color
& rFontRefColor
)
364 pShape
->getShapeStyleRefs()[XML_fontRef
].maPhClr
= rFontRefColor
;
365 std::vector
<oox::drawingml::ShapePtr
>& vChildren
= pShape
->getChildren();
366 for (auto const& child
: vChildren
)
368 applyFontRefColor(child
, rFontRefColor
);
376 class VmlFindNoteFunc
379 explicit VmlFindNoteFunc( const ScAddress
& rPos
);
380 bool operator()( const ::oox::vml::ShapeBase
& rShape
) const;
387 VmlFindNoteFunc::VmlFindNoteFunc( const ScAddress
& rPos
) :
393 bool VmlFindNoteFunc::operator()( const ::oox::vml::ShapeBase
& rShape
) const
395 const ::oox::vml::ClientData
* pClientData
= rShape
.getClientData();
396 return pClientData
&& (pClientData
->mnCol
== mnCol
) && (pClientData
->mnRow
== mnRow
);
401 VmlControlMacroAttacher::VmlControlMacroAttacher( const OUString
& rMacroName
,
402 const Reference
< XIndexContainer
>& rxCtrlFormIC
, sal_Int32 nCtrlIndex
, sal_Int32 nCtrlType
, sal_Int32 nDropStyle
) :
403 VbaMacroAttacherBase( rMacroName
),
404 mxCtrlFormIC( rxCtrlFormIC
),
405 mnCtrlIndex( nCtrlIndex
),
406 mnCtrlType( nCtrlType
),
407 mnDropStyle( nDropStyle
)
411 void VmlControlMacroAttacher::attachMacro( const OUString
& rMacroUrl
)
413 ScriptEventDescriptor aEventDesc
;
414 aEventDesc
.ScriptType
= "Script";
415 aEventDesc
.ScriptCode
= rMacroUrl
;
417 // editable drop downs are treated like edit boxes
418 bool bEditDropDown
= (mnCtrlType
== XML_Drop
) && (mnDropStyle
== XML_ComboEdit
);
419 sal_Int32 nCtrlType
= bEditDropDown
? XML_Edit
: mnCtrlType
;
426 aEventDesc
.ListenerType
= "XActionListener";
427 aEventDesc
.EventMethod
= "actionPerformed";
432 aEventDesc
.ListenerType
= "XMouseListener";
433 aEventDesc
.EventMethod
= "mouseReleased";
436 aEventDesc
.ListenerType
= "XTextListener";
437 aEventDesc
.EventMethod
= "textChanged";
441 aEventDesc
.ListenerType
= "XAdjustmentListener";
442 aEventDesc
.EventMethod
= "adjustmentValueChanged";
446 aEventDesc
.ListenerType
= "XChangeListener";
447 aEventDesc
.EventMethod
= "changed";
450 OSL_ENSURE( false, "VmlControlMacroAttacher::attachMacro - unexpected object type" );
456 Reference
< XEventAttacherManager
> xEventMgr( mxCtrlFormIC
, UNO_QUERY_THROW
);
457 xEventMgr
->registerScriptEvent( mnCtrlIndex
, aEventDesc
);
464 VmlDrawing::VmlDrawing( const WorksheetHelper
& rHelper
) :
465 ::oox::vml::Drawing( rHelper
.getOoxFilter(), rHelper
.getDrawPage(), ::oox::vml::VMLDRAWING_EXCEL
),
466 WorksheetHelper( rHelper
),
467 maControlConv( rHelper
.getBaseFilter().getModel(), rHelper
.getBaseFilter().getGraphicHelper() )
469 // default font for legacy listboxes and dropdowns: Tahoma, 8pt
470 maListBoxFont
.moName
= "Tahoma";
471 maListBoxFont
.moColor
= "auto";
472 maListBoxFont
.monSize
= 160;
475 const ::oox::vml::ShapeBase
* VmlDrawing::getNoteShape( const ScAddress
& rPos
) const
477 return getShapes().findShape( VmlFindNoteFunc( rPos
) );
480 bool VmlDrawing::isShapeSupported( const ::oox::vml::ShapeBase
& rShape
) const
482 const ::oox::vml::ClientData
* pClientData
= rShape
.getClientData();
483 return !pClientData
|| (pClientData
->mnObjType
!= XML_Note
);
486 OUString
VmlDrawing::getShapeBaseName( const ::oox::vml::ShapeBase
& rShape
) const
488 if( const ::oox::vml::ClientData
* pClientData
= rShape
.getClientData() )
490 switch( pClientData
->mnObjType
)
492 case XML_Button
: return "Button";
493 case XML_Checkbox
: return "Check Box";
494 case XML_Dialog
: return "Dialog Frame";
495 case XML_Drop
: return "Drop Down";
496 case XML_Edit
: return "Edit Box";
497 case XML_GBox
: return "Group Box";
498 case XML_Label
: return "Label";
499 case XML_List
: return "List Box";
500 case XML_Note
: return "Comment";
501 case XML_Pict
: return (pClientData
->mbDde
|| getOleObjectInfo( rShape
.getShapeId() )) ? OUString( "Object" ) : OUString( "Picture" );
502 case XML_Radio
: return "Option Button";
503 case XML_Scroll
: return "Scroll Bar";
504 case XML_Spin
: return "Spinner";
507 return ::oox::vml::Drawing::getShapeBaseName( rShape
);
510 bool VmlDrawing::convertClientAnchor( Rectangle
& orShapeRect
, const OUString
& rShapeAnchor
) const
512 if( rShapeAnchor
.isEmpty() )
514 ShapeAnchor
aAnchor( *this );
515 aAnchor
.importVmlAnchor( rShapeAnchor
);
516 orShapeRect
= aAnchor
.calcAnchorRectHmm( getDrawPageSize() );
517 return (orShapeRect
.Width
>= 0) && (orShapeRect
.Height
>= 0);
520 Reference
< XShape
> VmlDrawing::createAndInsertClientXShape( const ::oox::vml::ShapeBase
& rShape
,
521 const Reference
< XShapes
>& rxShapes
, const Rectangle
& rShapeRect
) const
523 // simulate the legacy drawing controls with OLE form controls
524 OUString aShapeName
= rShape
.getShapeName();
525 const ::oox::vml::ClientData
* pClientData
= rShape
.getClientData();
526 if( !aShapeName
.isEmpty() && pClientData
)
528 Rectangle aShapeRect
= rShapeRect
;
529 const ::oox::vml::TextBox
* pTextBox
= rShape
.getTextBox();
530 EmbeddedControl
aControl( aShapeName
);
531 switch( pClientData
->mnObjType
)
535 AxCommandButtonModel
& rAxModel
= aControl
.createModel
< AxCommandButtonModel
>();
536 convertControlText( rAxModel
.maFontData
, rAxModel
.mnTextColor
, rAxModel
.maCaption
, pTextBox
, pClientData
->mnTextHAlign
);
537 rAxModel
.mnFlags
= AX_FLAGS_ENABLED
| AX_FLAGS_OPAQUE
| AX_FLAGS_WORDWRAP
;
538 rAxModel
.mnVerticalAlign
= pClientData
->mnTextVAlign
;
544 AxLabelModel
& rAxModel
= aControl
.createModel
< AxLabelModel
>();
545 convertControlText( rAxModel
.maFontData
, rAxModel
.mnTextColor
, rAxModel
.maCaption
, pTextBox
, pClientData
->mnTextHAlign
);
546 rAxModel
.mnFlags
= AX_FLAGS_ENABLED
| AX_FLAGS_WORDWRAP
;
547 rAxModel
.mnBorderStyle
= AX_BORDERSTYLE_NONE
;
548 rAxModel
.mnSpecialEffect
= AX_SPECIALEFFECT_FLAT
;
549 rAxModel
.mnVerticalAlign
= pClientData
->mnTextVAlign
;
555 bool bNumeric
= (pClientData
->mnVTEdit
== ::oox::vml::VML_CLIENTDATA_INTEGER
) || (pClientData
->mnVTEdit
== ::oox::vml::VML_CLIENTDATA_NUMBER
);
556 AxMorphDataModelBase
& rAxModel
= bNumeric
?
557 static_cast< AxMorphDataModelBase
& >( aControl
.createModel
< AxNumericFieldModel
>() ) :
558 static_cast< AxMorphDataModelBase
& >( aControl
.createModel
< AxTextBoxModel
>() );
559 convertControlText( rAxModel
.maFontData
, rAxModel
.mnTextColor
, rAxModel
.maValue
, pTextBox
, pClientData
->mnTextHAlign
);
560 setFlag( rAxModel
.mnFlags
, AX_FLAGS_MULTILINE
, pClientData
->mbMultiLine
);
561 setFlag( rAxModel
.mnScrollBars
, AX_SCROLLBAR_VERTICAL
, pClientData
->mbVScroll
);
562 if( pClientData
->mbSecretEdit
)
563 rAxModel
.mnPasswordChar
= '*';
569 AxFrameModel
& rAxModel
= aControl
.createModel
< AxFrameModel
>();
570 convertControlText( rAxModel
.maFontData
, rAxModel
.mnTextColor
, rAxModel
.maCaption
, pTextBox
, pClientData
->mnTextHAlign
);
571 rAxModel
.mnBorderStyle
= pClientData
->mbNo3D
? AX_BORDERSTYLE_SINGLE
: AX_BORDERSTYLE_NONE
;
572 rAxModel
.mnSpecialEffect
= pClientData
->mbNo3D
? AX_SPECIALEFFECT_FLAT
: AX_SPECIALEFFECT_BUMPED
;
574 /* Move top border of groupbox up by half font height, because
575 Excel specifies Y position of the groupbox border line
576 instead the top border of the caption text. */
577 if( const ::oox::vml::TextFontModel
* pFontModel
= pTextBox
? pTextBox
->getFirstFont() : nullptr )
579 sal_Int32 nFontHeightHmm
= o3tl::convert( pFontModel
->monSize
.value_or( 160 ), o3tl::Length::twip
, o3tl::Length::mm100
);
580 sal_Int32 nYDiff
= ::std::min
< sal_Int32
>( nFontHeightHmm
/ 2, aShapeRect
.Y
);
581 aShapeRect
.Y
-= nYDiff
;
582 aShapeRect
.Height
+= nYDiff
;
589 AxCheckBoxModel
& rAxModel
= aControl
.createModel
< AxCheckBoxModel
>();
590 convertControlText( rAxModel
.maFontData
, rAxModel
.mnTextColor
, rAxModel
.maCaption
, pTextBox
, pClientData
->mnTextHAlign
);
591 convertControlBackground( rAxModel
, rShape
);
592 rAxModel
.maValue
= OUString::number( pClientData
->mnChecked
);
593 rAxModel
.mnSpecialEffect
= pClientData
->mbNo3D
? AX_SPECIALEFFECT_FLAT
: AX_SPECIALEFFECT_SUNKEN
;
594 rAxModel
.mnVerticalAlign
= pClientData
->mnTextVAlign
;
595 bool bTriState
= (pClientData
->mnChecked
!= ::oox::vml::VML_CLIENTDATA_UNCHECKED
) && (pClientData
->mnChecked
!= ::oox::vml::VML_CLIENTDATA_CHECKED
);
596 rAxModel
.mnMultiSelect
= bTriState
? AX_SELECTION_MULTI
: AX_SELECTION_SINGLE
;
602 AxOptionButtonModel
& rAxModel
= aControl
.createModel
< AxOptionButtonModel
>();
604 // unique name to prevent autoGrouping with ActiveX controls and which a GroupBox may override - see vmldrawing.cxx.
605 rAxModel
.maGroupName
= "autoGroup_formControl";
606 convertControlText( rAxModel
.maFontData
, rAxModel
.mnTextColor
, rAxModel
.maCaption
, pTextBox
, pClientData
->mnTextHAlign
);
607 convertControlBackground( rAxModel
, rShape
);
608 rAxModel
.maValue
= OUString::number( pClientData
->mnChecked
);
609 rAxModel
.mnSpecialEffect
= pClientData
->mbNo3D
? AX_SPECIALEFFECT_FLAT
: AX_SPECIALEFFECT_SUNKEN
;
610 rAxModel
.mnVerticalAlign
= pClientData
->mnTextVAlign
;
616 AxListBoxModel
& rAxModel
= aControl
.createModel
< AxListBoxModel
>();
617 convertControlFontData( rAxModel
.maFontData
, rAxModel
.mnTextColor
, maListBoxFont
);
618 rAxModel
.mnBorderStyle
= pClientData
->mbNo3D2
? AX_BORDERSTYLE_SINGLE
: AX_BORDERSTYLE_NONE
;
619 rAxModel
.mnSpecialEffect
= pClientData
->mbNo3D2
? AX_SPECIALEFFECT_FLAT
: AX_SPECIALEFFECT_SUNKEN
;
620 switch( pClientData
->mnSelType
)
622 case XML_Single
: rAxModel
.mnMultiSelect
= AX_SELECTION_SINGLE
; break;
623 case XML_Multi
: rAxModel
.mnMultiSelect
= AX_SELECTION_MULTI
; break;
624 case XML_Extend
: rAxModel
.mnMultiSelect
= AX_SELECTION_EXTENDED
; break;
631 AxComboBoxModel
& rAxModel
= aControl
.createModel
< AxComboBoxModel
>();
632 convertControlFontData( rAxModel
.maFontData
, rAxModel
.mnTextColor
, maListBoxFont
);
633 rAxModel
.mnDisplayStyle
= AX_DISPLAYSTYLE_DROPDOWN
;
634 rAxModel
.mnShowDropButton
= AX_SHOWDROPBUTTON_ALWAYS
;
635 rAxModel
.mnBorderStyle
= pClientData
->mbNo3D2
? AX_BORDERSTYLE_SINGLE
: AX_BORDERSTYLE_NONE
;
636 rAxModel
.mnSpecialEffect
= pClientData
->mbNo3D2
? AX_SPECIALEFFECT_FLAT
: AX_SPECIALEFFECT_SUNKEN
;
637 rAxModel
.mnListRows
= pClientData
->mnDropLines
;
643 AxSpinButtonModel
& rAxModel
= aControl
.createModel
< AxSpinButtonModel
>();
644 rAxModel
.mnMin
= pClientData
->mnMin
;
645 rAxModel
.mnMax
= pClientData
->mnMax
;
646 rAxModel
.mnPosition
= pClientData
->mnVal
;
647 rAxModel
.mnSmallChange
= pClientData
->mnInc
;
653 AxScrollBarModel
& rAxModel
= aControl
.createModel
< AxScrollBarModel
>();
654 rAxModel
.mnMin
= pClientData
->mnMin
;
655 rAxModel
.mnMax
= pClientData
->mnMax
;
656 rAxModel
.mnPosition
= pClientData
->mnVal
;
657 rAxModel
.mnSmallChange
= pClientData
->mnInc
;
658 rAxModel
.mnLargeChange
= pClientData
->mnPage
;
664 // fake with a group box
665 AxFrameModel
& rAxModel
= aControl
.createModel
< AxFrameModel
>();
666 convertControlText( rAxModel
.maFontData
, rAxModel
.mnTextColor
, rAxModel
.maCaption
, pTextBox
, XML_Left
);
667 rAxModel
.mnBorderStyle
= AX_BORDERSTYLE_SINGLE
;
668 rAxModel
.mnSpecialEffect
= AX_SPECIALEFFECT_FLAT
;
673 if( ControlModelBase
* pAxModel
= aControl
.getModel() )
675 // create the control shape
676 pAxModel
->maSize
.first
= aShapeRect
.Width
;
677 pAxModel
->maSize
.second
= aShapeRect
.Height
;
678 sal_Int32 nCtrlIndex
= -1;
679 Reference
< XShape
> xShape
= createAndInsertXControlShape( aControl
, rxShapes
, aShapeRect
, nCtrlIndex
);
681 // control shape macro
682 if( xShape
.is() && (nCtrlIndex
>= 0) && !pClientData
->maFmlaMacro
.isEmpty() )
684 OUString aMacroName
= getFormulaParser().importMacroName( pClientData
->maFmlaMacro
);
685 if( !aMacroName
.isEmpty() )
687 Reference
< XIndexContainer
> xFormIC
= getControlForm().getXForm();
688 VbaMacroAttacherRef xAttacher
= std::make_shared
<VmlControlMacroAttacher
>( aMacroName
, xFormIC
, nCtrlIndex
, pClientData
->mnObjType
, pClientData
->mnDropStyle
);
689 getBaseFilter().getVbaProject().registerMacroAttacher( xAttacher
);
697 return Reference
< XShape
>();
700 void VmlDrawing::notifyXShapeInserted( const Reference
< XShape
>& rxShape
,
701 const Rectangle
& rShapeRect
, const ::oox::vml::ShapeBase
& rShape
, bool bGroupChild
)
703 // collect all shape positions in the WorksheetHelper base class (but not children of group shapes)
705 extendShapeBoundingBox( rShapeRect
);
707 // convert settings from VML client data
708 const ::oox::vml::ClientData
* pClientData
= rShape
.getClientData();
712 // specific settings for embedded form controls
715 Reference
< XControlShape
> xCtrlShape( rxShape
, UNO_QUERY_THROW
);
716 Reference
< XControlModel
> xCtrlModel( xCtrlShape
->getControl(), UNO_SET_THROW
);
717 PropertySet
aPropSet( xCtrlModel
);
720 aPropSet
.setProperty( PROP_Printable
, pClientData
->mbPrintObject
);
722 // control source links
723 if( !pClientData
->maFmlaLink
.isEmpty() || !pClientData
->maFmlaRange
.isEmpty() )
724 maControlConv
.bindToSources( xCtrlModel
, pClientData
->maFmlaLink
, pClientData
->maFmlaRange
, getSheetIndex() );
731 // private --------------------------------------------------------------------
733 sal_uInt32
VmlDrawing::convertControlTextColor( std::u16string_view aTextColor
) const
735 // color attribute not present or 'auto' - use passed default color
736 if( aTextColor
.empty() || o3tl::equalsIgnoreAsciiCase( aTextColor
, u
"auto" ) )
737 return AX_SYSCOLOR_WINDOWTEXT
;
739 if( aTextColor
[ 0 ] == '#' )
741 // RGB colors in the format '#RRGGBB'
742 if( aTextColor
.size() == 7 )
743 return OleHelper::encodeOleColor( o3tl::toUInt32(aTextColor
.substr( 1 ), 16) );
745 // RGB colors in the format '#RGB'
746 if( aTextColor
.size() == 4 )
748 sal_Int32 nR
= o3tl::toUInt32(aTextColor
.substr( 1, 1 ), 16) * 0x11;
749 sal_Int32 nG
= o3tl::toUInt32(aTextColor
.substr( 2, 1 ), 16) * 0x11;
750 sal_Int32 nB
= o3tl::toUInt32(aTextColor
.substr( 3, 1 ), 16) * 0x11;
751 return OleHelper::encodeOleColor( (nR
<< 16) | (nG
<< 8) | nB
);
754 OSL_ENSURE( false, OStringBuffer( "VmlDrawing::convertControlTextColor - invalid color name '" ).
755 append( OUStringToOString( aTextColor
, RTL_TEXTENCODING_ASCII_US
) ).append( '\'' ).getStr() );
756 return AX_SYSCOLOR_WINDOWTEXT
;
759 const GraphicHelper
& rGraphicHelper
= getBaseFilter().getGraphicHelper();
761 /* Predefined color names or system color names (resolve to RGB to detect
762 valid color name). */
763 sal_Int32 nColorToken
= AttributeConversion::decodeToken( aTextColor
);
764 ::Color nRgbValue
= Color::getVmlPresetColor( nColorToken
, API_RGB_TRANSPARENT
);
765 if( nRgbValue
== API_RGB_TRANSPARENT
)
766 nRgbValue
= rGraphicHelper
.getSystemColor( nColorToken
);
767 if( nRgbValue
!= API_RGB_TRANSPARENT
)
768 return OleHelper::encodeOleColor( nRgbValue
);
771 return OleHelper::encodeOleColor( rGraphicHelper
.getPaletteColor( o3tl::toInt32(aTextColor
) ) );
774 void VmlDrawing::convertControlFontData( AxFontData
& rAxFontData
, sal_uInt32
& rnOleTextColor
, const ::oox::vml::TextFontModel
& rFontModel
) const
776 if( rFontModel
.moName
.has_value() )
777 rAxFontData
.maFontName
= rFontModel
.moName
.value();
779 // font height: convert from twips to points, then to internal representation of AX controls
780 rAxFontData
.setHeightPoints( static_cast< sal_Int16
>( (rFontModel
.monSize
.value_or( 200 ) + 10) / 20 ) );
783 rAxFontData
.mnFontEffects
= AxFontFlags::NONE
;
784 setFlag( rAxFontData
.mnFontEffects
, AxFontFlags::Bold
, rFontModel
.mobBold
.value_or( false ) );
785 setFlag( rAxFontData
.mnFontEffects
, AxFontFlags::Italic
, rFontModel
.mobItalic
.value_or( false ) );
786 setFlag( rAxFontData
.mnFontEffects
, AxFontFlags::Strikeout
, rFontModel
.mobStrikeout
.value_or( false ) );
787 sal_Int32 nUnderline
= rFontModel
.monUnderline
.value_or( XML_none
);
788 setFlag( rAxFontData
.mnFontEffects
, AxFontFlags::Underline
, nUnderline
!= XML_none
);
789 rAxFontData
.mbDblUnderline
= nUnderline
== XML_double
;
792 rnOleTextColor
= convertControlTextColor( rFontModel
.moColor
.value_or( OUString() ) );
795 void VmlDrawing::convertControlText( AxFontData
& rAxFontData
, sal_uInt32
& rnOleTextColor
,
796 OUString
& rCaption
, const ::oox::vml::TextBox
* pTextBox
, sal_Int32 nTextHAlign
) const
800 rCaption
= pTextBox
->getText();
801 if( const ::oox::vml::TextFontModel
* pFontModel
= pTextBox
->getFirstFont() )
802 convertControlFontData( rAxFontData
, rnOleTextColor
, *pFontModel
);
805 switch( nTextHAlign
)
807 case XML_Left
: rAxFontData
.mnHorAlign
= AxHorizontalAlign::Left
; break;
808 case XML_Center
: rAxFontData
.mnHorAlign
= AxHorizontalAlign::Center
; break;
809 case XML_Right
: rAxFontData
.mnHorAlign
= AxHorizontalAlign::Right
; break;
810 default: rAxFontData
.mnHorAlign
= AxHorizontalAlign::Left
;
814 void VmlDrawing::convertControlBackground( AxMorphDataModelBase
& rAxModel
, const ::oox::vml::ShapeBase
& rShape
) const
816 const ::oox::vml::FillModel
& rFillModel
= rShape
.getTypeModel().maFillModel
;
817 bool bHasFill
= rFillModel
.moFilled
.value_or( true );
818 setFlag( rAxModel
.mnFlags
, AX_FLAGS_OPAQUE
, bHasFill
);
821 const GraphicHelper
& rGraphicHelper
= getBaseFilter().getGraphicHelper();
822 ::Color nSysWindowColor
= rGraphicHelper
.getSystemColor( XML_window
, API_RGB_WHITE
);
823 ::oox::drawingml::Color aColor
= ::oox::vml::ConversionHelper::decodeColor( rGraphicHelper
, rFillModel
.moColor
, rFillModel
.moOpacity
, nSysWindowColor
);
824 ::Color nRgbValue
= aColor
.getColor( rGraphicHelper
);
825 rAxModel
.mnBackColor
= OleHelper::encodeOleColor( nRgbValue
);
829 VmlDrawingFragment::VmlDrawingFragment( const WorksheetHelper
& rHelper
, const OUString
& rFragmentPath
) :
830 ::oox::vml::DrawingFragment( rHelper
.getOoxFilter(), rFragmentPath
, rHelper
.getVmlDrawing() ),
831 WorksheetHelper( rHelper
)
835 void VmlDrawingFragment::finalizeImport()
837 ::oox::vml::DrawingFragment::finalizeImport();
838 getVmlDrawing().convertAndInsert();
843 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */