LibreOffice Module svx (master)  1
viewcontactoftableobj.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 
22 #include <svx/svdotable.hxx>
23 #include <com/sun/star/table/XTable.hpp>
30 #include <editeng/borderline.hxx>
37 #include <svx/framelink.hxx>
38 #include <svx/framelinkarray.hxx>
39 #include <svx/sdooitm.hxx>
40 #include <vcl/canvastools.hxx>
41 #include <o3tl/unit_conversion.hxx>
42 
43 #include <cell.hxx>
44 #include "tablelayouter.hxx"
45 
46 
48 using namespace com::sun::star;
49 
50 
52 {
53  namespace {
54 
55  class SdrCellPrimitive2D : public BufferedDecompositionPrimitive2D
56  {
57  private:
59  attribute::SdrFillTextAttribute maSdrFTAttribute;
60 
61  protected:
62  // local decomposition.
63  virtual void create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const override;
64 
65  public:
66  SdrCellPrimitive2D(
67  const basegfx::B2DHomMatrix& rTransform,
68  const attribute::SdrFillTextAttribute& rSdrFTAttribute)
69  : BufferedDecompositionPrimitive2D(),
70  maTransform(rTransform),
71  maSdrFTAttribute(rSdrFTAttribute)
72  {
73  }
74 
75  // data access
76  const basegfx::B2DHomMatrix& getTransform() const { return maTransform; }
77  const attribute::SdrFillTextAttribute& getSdrFTAttribute() const { return maSdrFTAttribute; }
78 
79  // compare operator
80  virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
81 
82  // provide unique ID
83  virtual sal_uInt32 getPrimitive2DID() const override;
84  };
85 
86  }
87 
88  void SdrCellPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const
89  {
90  // prepare unit polygon
92 
93  // add fill
94  if(!getSdrFTAttribute().getFill().isDefault())
95  {
96  basegfx::B2DPolyPolygon aTransformed(aUnitPolyPolygon);
97 
98  aTransformed.transform(getTransform());
99  rContainer.push_back(
101  aTransformed,
102  getSdrFTAttribute().getFill(),
103  getSdrFTAttribute().getFillFloatTransGradient()));
104  }
105  else
106  {
107  // if no fill create one for HitTest and BoundRect fallback
108  rContainer.push_back(
110  true,
111  aUnitPolyPolygon,
112  getTransform()));
113  }
114 
115  // add text
116  if(!getSdrFTAttribute().getText().isDefault())
117  {
118  rContainer.push_back(
120  aUnitPolyPolygon,
121  getTransform(),
122  getSdrFTAttribute().getText(),
123  attribute::SdrLineAttribute(),
124  true,
125  false));
126  }
127  }
128 
129  bool SdrCellPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
130  {
131  if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
132  {
133  const SdrCellPrimitive2D& rCompare = static_cast<const SdrCellPrimitive2D&>(rPrimitive);
134 
135  return (getTransform() == rCompare.getTransform()
136  && getSdrFTAttribute() == rCompare.getSdrFTAttribute());
137  }
138 
139  return false;
140  }
141 
142  // provide unique ID
144 
145 } // end of namespace
146 
147 namespace sdr::contact
148 {
150  const sdr::table::TableLayouter& rLayouter,
151  sal_Int32 nX,
152  sal_Int32 nY,
153  bool bHorizontal,
154  sal_Int32 nColCount,
155  sal_Int32 nRowCount,
156  bool bIsRTL)
157  {
158  if(nX >= 0 && nX <= nColCount && nY >= 0 && nY <= nRowCount)
159  {
160  const SvxBorderLine* pLine = rLayouter.getBorderLine(nX, nY, bHorizontal);
161 
162  if(pLine)
163  {
164  // copy line content
165  SvxBorderLine aLine(*pLine);
166 
167  // check for mirroring. This shall always be done when it is
168  // not a top- or rightmost line
169  bool bMirror(aLine.isDouble());
170 
171  if(bMirror)
172  {
173  if(bHorizontal)
174  {
175  // mirror all bottom lines
176  bMirror = (0 != nY);
177  }
178  else
179  {
180  // mirror all left lines
181  bMirror = (bIsRTL ? 0 != nX : nX != nColCount);
182  }
183  }
184 
185  if(bMirror)
186  {
187  aLine.SetMirrorWidths( );
188  }
189 
190  constexpr double fTwipsToMM(
192  return svx::frame::Style(&aLine, fTwipsToMM);
193  }
194  }
195 
196  // no success, copy empty line
197  return svx::frame::Style();
198  }
199 
200  drawinglayer::primitive2d::Primitive2DContainer ViewContactOfTableObj::createViewIndependentPrimitive2DSequence() const
201  {
202  const sdr::table::SdrTableObj& rTableObj = static_cast<const sdr::table::SdrTableObj&>(GetSdrObject());
203  const uno::Reference< css::table::XTable > xTable = rTableObj.getTable();
204 
205  if(xTable.is())
206  {
207  // create primitive representation for table. Cell info goes
208  // directly to aRetval, Border info to aBorderSequence and added
209  // later to get the correct overlapping
212  const sal_Int32 nRowCount(xTable->getRowCount());
213  const sal_Int32 nColCount(xTable->getColumnCount());
214  const sal_Int32 nAllCount(nRowCount * nColCount);
215 
216  if(nAllCount)
217  {
218  const sdr::table::TableLayouter& rTableLayouter(rTableObj.getTableLayouter());
219  const bool bIsRTL(css::text::WritingMode_RL_TB == rTableObj.GetWritingMode());
220  sdr::table::CellPos aCellPos;
221  sdr::table::CellRef xCurrentCell;
222  basegfx::B2IRectangle aCellArea;
223 
224  // create range using the model data directly. This is in SdrTextObj::aRect which i will access using
225  // GetGeoRect() to not trigger any calculations. It's the unrotated geometry.
227 
228  // To create the CellBorderPrimitives, use the tolling from svx::frame::Array
229  // which is capable of creating the needed visualization. Fill it during the
230  // anyways needed run over the table.
231  svx::frame::Array aArray;
232 
233  // initialize CellBorderArray for primitive creation
234  aArray.Initialize(nColCount, nRowCount);
235 
236  // create single primitives per cell
237  for(aCellPos.mnRow = 0; aCellPos.mnRow < nRowCount; aCellPos.mnRow++)
238  {
239  // add RowHeight to CellBorderArray for primitive creation
240  aArray.SetRowHeight(aCellPos.mnRow, rTableLayouter.getRowHeight(aCellPos.mnRow));
241 
242  for(aCellPos.mnCol = 0; aCellPos.mnCol < nColCount; aCellPos.mnCol++)
243  {
244  // add ColWidth to CellBorderArray for primitive creation, only
245  // needs to be done in the 1st run
246  if(0 == aCellPos.mnRow)
247  {
248  aArray.SetColWidth(aCellPos.mnCol, rTableLayouter.getColumnWidth(aCellPos.mnCol));
249  }
250 
251  // access the cell
252  xCurrentCell.set(dynamic_cast< sdr::table::Cell* >(xTable->getCellByPosition(aCellPos.mnCol, aCellPos.mnRow).get()));
253 
254  if(xCurrentCell.is())
255  {
256  // copy styles for current cell to CellBorderArray for primitive creation
257  aArray.SetCellStyleLeft(aCellPos.mnCol, aCellPos.mnRow, impGetLineStyle(rTableLayouter, aCellPos.mnCol, aCellPos.mnRow, false, nColCount, nRowCount, bIsRTL));
258  aArray.SetCellStyleRight(aCellPos.mnCol, aCellPos.mnRow, impGetLineStyle(rTableLayouter, aCellPos.mnCol + 1, aCellPos.mnRow, false, nColCount, nRowCount, bIsRTL));
259  aArray.SetCellStyleTop(aCellPos.mnCol, aCellPos.mnRow, impGetLineStyle(rTableLayouter, aCellPos.mnCol, aCellPos.mnRow, true, nColCount, nRowCount, bIsRTL));
260  aArray.SetCellStyleBottom(aCellPos.mnCol, aCellPos.mnRow, impGetLineStyle(rTableLayouter, aCellPos.mnCol, aCellPos.mnRow + 1, true, nColCount, nRowCount, bIsRTL));
261 
262  // ignore merged cells (all except the top-left of a merged cell)
263  if(!xCurrentCell->isMerged())
264  {
265  // check if we are the top-left of a merged cell
266  const sal_Int32 nXSpan(xCurrentCell->getColumnSpan());
267  const sal_Int32 nYSpan(xCurrentCell->getRowSpan());
268 
269  if(nXSpan > 1 || nYSpan > 1)
270  {
271  // if merged, set so at CellBorderArray for primitive creation
272  aArray.SetMergedRange(aCellPos.mnCol, aCellPos.mnRow, aCellPos.mnCol + nXSpan - 1, aCellPos.mnRow + nYSpan - 1);
273  }
274  }
275  }
276 
277  if(xCurrentCell.is() && !xCurrentCell->isMerged())
278  {
279  if(rTableLayouter.getCellArea(xCurrentCell, aCellPos, aCellArea))
280  {
281  // create cell transformation matrix
282  basegfx::B2DHomMatrix aCellMatrix;
283  aCellMatrix.set(0, 0, static_cast<double>(aCellArea.getWidth()));
284  aCellMatrix.set(1, 1, static_cast<double>(aCellArea.getHeight()));
285  aCellMatrix.set(0, 2, static_cast<double>(aCellArea.getMinX()) + aObjectRange.getMinX());
286  aCellMatrix.set(1, 2, static_cast<double>(aCellArea.getMinY()) + aObjectRange.getMinY());
287 
288  // handle cell fillings and text
289  const SfxItemSet& rCellItemSet = xCurrentCell->GetItemSet();
290  const sal_uInt32 nTextIndex(nColCount * aCellPos.mnRow + aCellPos.mnCol);
291  const SdrText* pSdrText = rTableObj.getText(nTextIndex);
293 
294  if(pSdrText)
295  {
296  // #i101508# take cell's local text frame distances into account
297  const sal_Int32 nLeft(xCurrentCell->GetTextLeftDistance());
298  const sal_Int32 nRight(xCurrentCell->GetTextRightDistance());
299  const sal_Int32 nUpper(xCurrentCell->GetTextUpperDistance());
300  const sal_Int32 nLower(xCurrentCell->GetTextLowerDistance());
301 
303  rCellItemSet,
304  pSdrText,
305  &nLeft,
306  &nUpper,
307  &nRight,
308  &nLower);
309  }
310  else
311  {
313  rCellItemSet,
314  pSdrText);
315  }
316 
317  // always create cell primitives for BoundRect and HitTest
318  {
320  new drawinglayer::primitive2d::SdrCellPrimitive2D(
321  aCellMatrix, aAttribute));
322  aRetval.append(xCellReference);
323  }
324 
325  // Create cell primitive without text.
326  aAttribute
328  rCellItemSet, nullptr);
330  xCellReference(
331  new drawinglayer::primitive2d::SdrCellPrimitive2D(
332  aCellMatrix, aAttribute));
333  aRetvalForShadow.append(xCellReference);
334  }
335  }
336  }
337  }
338 
339  // now create all CellBorderPrimitives
340  const drawinglayer::primitive2d::Primitive2DContainer aCellBorderPrimitives(aArray.CreateB2DPrimitiveArray());
341 
342  if(!aCellBorderPrimitives.empty())
343  {
344  // this is already scaled (due to Table in non-uniform coordinates), so
345  // first transform removing scale
346  basegfx::B2DHomMatrix aTransform(
348  1.0 / aObjectRange.getWidth(),
349  1.0 / aObjectRange.getHeight()));
350 
351  // If RTL, mirror the whole unified table in X and move right.
352  // This is much easier than taking this into account for the whole
353  // index calculations
354  if(bIsRTL)
355  {
356  aTransform.scale(-1.0, 1.0);
357  aTransform.translate(1.0, 0.0);
358  }
359 
360  // create object matrix
361  const GeoStat& rGeoStat(rTableObj.GetGeoStat());
362  const double fShearX(-rGeoStat.mfTanShearAngle);
363  const double fRotate(rGeoStat.nRotationAngle ? (36000 - rGeoStat.nRotationAngle.get()) * F_PI18000 : 0.0);
365  aObjectRange.getWidth(), aObjectRange.getHeight(), fShearX, fRotate,
366  aObjectRange.getMinX(), aObjectRange.getMinY()));
367 
368  // add object matrix to transform. By doing so theoretically
369  // CellBorders could be also rotated/sheared for the first time ever.
370  // To completely make that work, the primitives already created in
371  // aRetval would also have to be based on ObjectMatrix, not only on
372  // ObjectRange as it currently is.
373  aTransform *= aObjectMatrix;
374 
375  // create a transform primitive with this and embed CellBorders
376  // and append to retval
377  aRetval.append(
379  aTransform,
380  aCellBorderPrimitives));
381 
382  // Borders are always the same for shadow as well.
384  aTransform, aCellBorderPrimitives));
385  }
386  }
387 
388  if(!aRetval.empty())
389  {
390  // check and create evtl. shadow for created content
391  const SfxItemSet& rObjectItemSet = rTableObj.GetMergedItemSet();
392  const drawinglayer::attribute::SdrShadowAttribute aNewShadowAttribute(
394 
395  if(!aNewShadowAttribute.isDefault())
396  {
397  bool bDirectShadow
398  = rObjectItemSet.Get(SDRATTR_SHADOW, /*bSrchInParent=*/false)
399  .GetValue();
400  if (bDirectShadow)
401  {
402  // Shadow as direct formatting: no shadow for text, to be compatible
403  // with PowerPoint.
404  basegfx::B2DHomMatrix aMatrix;
406  aRetval, aNewShadowAttribute, aMatrix, &aRetvalForShadow);
407  }
408  else
409  {
410  // Shadow as style: shadow for text, to be backwards-compatible.
412  aRetval, aNewShadowAttribute);
413  }
414  }
415  }
416 
417  return aRetval;
418  }
419  else
420  {
421  // take unrotated snap rect (direct model data) for position and size
423 
424  // create object matrix
425  const GeoStat& rGeoStat(rTableObj.GetGeoStat());
426  const double fShearX(-rGeoStat.mfTanShearAngle);
427  const double fRotate(rGeoStat.nRotationAngle ? (36000 - rGeoStat.nRotationAngle.get()) * F_PI18000 : 0.0);
429  aObjectRange.getWidth(), aObjectRange.getHeight(), fShearX, fRotate,
430  aObjectRange.getMinX(), aObjectRange.getMinY()));
431 
432  // created an invisible outline for the cases where no visible content exists
435  aObjectMatrix));
436 
438  }
439  }
440 
441  ViewContactOfTableObj::ViewContactOfTableObj(sdr::table::SdrTableObj& rTableObj)
442  : ViewContactOfSdrObj(rTableObj)
443  {
444  }
445 
447  {
448  }
449 } // end of namespace
450 
451 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
css::uno::Reference< css::table::XTable > getTable() const
Definition: svdotable.cxx:932
static svx::frame::Style impGetLineStyle(const sdr::table::TableLayouter &rLayouter, sal_Int32 nX, sal_Int32 nY, bool bHorizontal, sal_Int32 nColCount, sal_Int32 nRowCount, bool bIsRTL)
void set(sal_uInt16 nRow, sal_uInt16 nColumn, double fValue)
double getHeight() const
B2DPolygon const & createUnitPolygon()
sal_Int32 getMinY() const
#define PRIMITIVE2D_ID_SDRCELLPRIMITIVE2D
ImplPrimitive2DIDBlock(LazyControlCreationPrimitive2D, PRIMITIVE2D_ID_SDRCONTROLPRIMITIVE2D) ViewObjectContactOfUnoControl
editeng::SvxBorderLine * getBorderLine(sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal) const
returns the requested borderline in rpBorderLine or a null pointer if there is no border at this edge...
virtual SdrText * getText(sal_Int32 nIndex) const override
Returns the nth available text.
Definition: svdotable.cxx:1339
Primitive2DContainer createEmbeddedShadowPrimitive(const Primitive2DContainer &rContent, const attribute::SdrShadowAttribute &rShadow, const basegfx::B2DHomMatrix &rObjectMatrix, const Primitive2DContainer *pContentForShadow)
double getWidth() const
B2DHomMatrix createScaleB2DHomMatrix(double fScaleX, double fScaleY)
B2DHomMatrix createScaleShearXRotateTranslateB2DHomMatrix(double fScaleX, double fScaleY, double fShearX, double fRadiant, double fTranslateX, double fTranslateY)
The transformation of a rectangle into a polygon, by using angle parameters from GeoStat.
Definition: svdtrans.hxx:215
attribute::SdrFillTextAttribute maSdrFTAttribute
const GeoStat & GetGeoStat() const
Definition: svdotext.hxx:402
constexpr TypedWhichId< SdrOnOffItem > SDRATTR_SHADOW(SDRATTR_SHADOW_FIRST+0)
constexpr auto convert(N n, sal_Int64 mul, sal_Int64 div)
basegfx::B2DHomMatrix maTransform
sal_Int64 getWidth() const
void scale(double fX, double fY)
virtual void append(const Primitive2DReference &) override
void Initialize(size_t nWidth, size_t nHeight)
Reinitializes the array with the specified size.
sal_Int32 getMinX() const
Primitive2DReference createTextPrimitive(const basegfx::B2DPolyPolygon &rUnitPolyPolygon, const basegfx::B2DHomMatrix &rObjectTransform, const attribute::SdrTextAttribute &rText, const attribute::SdrLineAttribute &rStroke, bool bCellText, bool bWordWrap)
css::text::WritingMode GetWritingMode() const
Definition: svdotable.cxx:2026
basegfx::B2DRange b2DRectangleFromRectangle(const ::tools::Rectangle &rRect)
css::uno::Reference< css::graphic::XPrimitive2D > Primitive2DReference
double getMinY() const
sal_Int64 getHeight() const
bool isDouble() const
const SfxPoolItem & Get(sal_uInt16 nWhich, bool bSrchInParent=true) const
attribute::SdrShadowAttribute createNewSdrShadowAttribute(const SfxItemSet &rSet)
const SfxItemSet & GetMergedItemSet() const
Definition: svdobj.cxx:1957
Primitive2DReference createHiddenGeometryPrimitives2D(const basegfx::B2DHomMatrix &rMatrix)
#define F_PI18000
Contains the widths of primary and secondary line of a frame style.
Definition: framelink.hxx:100
attribute::SdrFillTextAttribute createNewSdrFillTextAttribute(const SfxItemSet &rSet, const SdrText *pText, const sal_Int32 *pLeft, const sal_Int32 *pUpper, const sal_Int32 *pRight, const sal_Int32 *pLower)
void translate(double fX, double fY)
double getMinX() const
Stores frame styles of an array of cells, supports merged ranges.
bool operator==(const ScCsvLayoutData &rData1, const ScCsvLayoutData &rData2)
const tools::Rectangle & GetGeoRect() const
Definition: svdotext.cxx:1470
const TableLayouter & getTableLayouter() const
Definition: svdotable.cxx:1410
Primitive2DReference createPolyPolygonFillPrimitive(const basegfx::B2DPolyPolygon &rPolyPolygon, const attribute::SdrFillAttribute &rFill, const attribute::FillGradientAttribute &rFillGradient)