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>
38 #include <svx/framelink.hxx>
39 #include <svx/framelinkarray.hxx>
40 #include <svx/sdooitm.hxx>
41 #include <vcl/canvastools.hxx>
42 #include <o3tl/unit_conversion.hxx>
43 #include <svx/xfltrit.hxx>
44 
45 #include <cell.hxx>
46 #include "tablelayouter.hxx"
47 
48 
50 using namespace com::sun::star;
51 
52 
54 {
55  namespace {
56 
57  class SdrCellPrimitive2D : public BufferedDecompositionPrimitive2D
58  {
59  private:
61  attribute::SdrFillTextAttribute maSdrFTAttribute;
62 
63  protected:
64  // local decomposition.
65  virtual void create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const override;
66 
67  public:
68  SdrCellPrimitive2D(
69  const basegfx::B2DHomMatrix& rTransform,
70  const attribute::SdrFillTextAttribute& rSdrFTAttribute)
71  : maTransform(rTransform),
72  maSdrFTAttribute(rSdrFTAttribute)
73  {
74  }
75 
76  // data access
77  const basegfx::B2DHomMatrix& getTransform() const { return maTransform; }
78  const attribute::SdrFillTextAttribute& getSdrFTAttribute() const { return maSdrFTAttribute; }
79 
80  // compare operator
81  virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
82 
83  // provide unique ID
84  virtual sal_uInt32 getPrimitive2DID() const override;
85  };
86 
87  }
88 
89  void SdrCellPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const
90  {
91  // prepare unit polygon
93 
94  // add fill
95  if(!getSdrFTAttribute().getFill().isDefault())
96  {
97  basegfx::B2DPolyPolygon aTransformed(aUnitPolyPolygon);
98 
99  aTransformed.transform(getTransform());
100  rContainer.push_back(
102  aTransformed,
103  getSdrFTAttribute().getFill(),
104  getSdrFTAttribute().getFillFloatTransGradient()));
105  }
106  else
107  {
108  // if no fill create one for HitTest and BoundRect fallback
109  rContainer.push_back(
111  true,
112  aUnitPolyPolygon,
113  getTransform()));
114  }
115 
116  // add text
117  if(!getSdrFTAttribute().getText().isDefault())
118  {
119  rContainer.push_back(
121  aUnitPolyPolygon,
122  getTransform(),
123  getSdrFTAttribute().getText(),
124  attribute::SdrLineAttribute(),
125  true,
126  false));
127  }
128  }
129 
130  bool SdrCellPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
131  {
132  if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
133  {
134  const SdrCellPrimitive2D& rCompare = static_cast<const SdrCellPrimitive2D&>(rPrimitive);
135 
136  return (getTransform() == rCompare.getTransform()
137  && getSdrFTAttribute() == rCompare.getSdrFTAttribute());
138  }
139 
140  return false;
141  }
142 
143  // provide unique ID
144  sal_uInt32 SdrCellPrimitive2D::getPrimitive2DID() const
145  {
147  }
148 
149 } // end of namespace
150 
151 namespace sdr::contact
152 {
154  const sdr::table::TableLayouter& rLayouter,
155  sal_Int32 nX,
156  sal_Int32 nY,
157  bool bHorizontal,
158  sal_Int32 nColCount,
159  sal_Int32 nRowCount,
160  bool bIsRTL)
161  {
162  if(nX >= 0 && nX <= nColCount && nY >= 0 && nY <= nRowCount)
163  {
164  const SvxBorderLine* pLine = rLayouter.getBorderLine(nX, nY, bHorizontal);
165 
166  if(pLine)
167  {
168  // copy line content
169  SvxBorderLine aLine(*pLine);
170 
171  // check for mirroring. This shall always be done when it is
172  // not a top- or rightmost line
173  bool bMirror(aLine.isDouble());
174 
175  if(bMirror)
176  {
177  if(bHorizontal)
178  {
179  // mirror all bottom lines
180  bMirror = (0 != nY);
181  }
182  else
183  {
184  // mirror all left lines
185  bMirror = (bIsRTL ? 0 != nX : nX != nColCount);
186  }
187  }
188 
189  if(bMirror)
190  {
191  aLine.SetMirrorWidths( );
192  }
193 
194  constexpr double fTwipsToMM(
196  return svx::frame::Style(&aLine, fTwipsToMM);
197  }
198  }
199 
200  // no success, copy empty line
201  return svx::frame::Style();
202  }
203 
204  drawinglayer::primitive2d::Primitive2DContainer ViewContactOfTableObj::createViewIndependentPrimitive2DSequence() const
205  {
206  const sdr::table::SdrTableObj& rTableObj = static_cast<const sdr::table::SdrTableObj&>(GetSdrObject());
207  const uno::Reference< css::table::XTable > xTable = rTableObj.getTable();
208 
209  if(xTable.is())
210  {
211  // create primitive representation for table. Cell info goes
212  // directly to aRetval, Border info to aBorderSequence and added
213  // later to get the correct overlapping
216  const sal_Int32 nRowCount(xTable->getRowCount());
217  const sal_Int32 nColCount(xTable->getColumnCount());
218  const sal_Int32 nAllCount(nRowCount * nColCount);
219 
220  if(nAllCount)
221  {
222  const sdr::table::TableLayouter& rTableLayouter(rTableObj.getTableLayouter());
223  const bool bIsRTL(css::text::WritingMode_RL_TB == rTableObj.GetWritingMode());
224  sdr::table::CellPos aCellPos;
225  sdr::table::CellRef xCurrentCell;
226  basegfx::B2IRectangle aCellArea;
227 
228  // create range using the model data directly. This is in SdrTextObj::aRect which i will access using
229  // GetGeoRect() to not trigger any calculations. It's the unrotated geometry.
231 
232  // To create the CellBorderPrimitives, use the tolling from svx::frame::Array
233  // which is capable of creating the needed visualization. Fill it during the
234  // anyways needed run over the table.
235  svx::frame::Array aArray;
236 
237  // initialize CellBorderArray for primitive creation
238  aArray.Initialize(nColCount, nRowCount);
239 
240  // create single primitives per cell
241  for(aCellPos.mnRow = 0; aCellPos.mnRow < nRowCount; aCellPos.mnRow++)
242  {
243  // add RowHeight to CellBorderArray for primitive creation
244  aArray.SetRowHeight(aCellPos.mnRow, rTableLayouter.getRowHeight(aCellPos.mnRow));
245 
246  for(aCellPos.mnCol = 0; aCellPos.mnCol < nColCount; aCellPos.mnCol++)
247  {
248  // add ColWidth to CellBorderArray for primitive creation, only
249  // needs to be done in the 1st run
250  if(0 == aCellPos.mnRow)
251  {
252  aArray.SetColWidth(aCellPos.mnCol, rTableLayouter.getColumnWidth(aCellPos.mnCol));
253  }
254 
255  // access the cell
256  xCurrentCell.set(dynamic_cast< sdr::table::Cell* >(xTable->getCellByPosition(aCellPos.mnCol, aCellPos.mnRow).get()));
257 
258  if(xCurrentCell.is())
259  {
260  // copy styles for current cell to CellBorderArray for primitive creation
261  aArray.SetCellStyleLeft(aCellPos.mnCol, aCellPos.mnRow, impGetLineStyle(rTableLayouter, aCellPos.mnCol, aCellPos.mnRow, false, nColCount, nRowCount, bIsRTL));
262  aArray.SetCellStyleRight(aCellPos.mnCol, aCellPos.mnRow, impGetLineStyle(rTableLayouter, aCellPos.mnCol + 1, aCellPos.mnRow, false, nColCount, nRowCount, bIsRTL));
263  aArray.SetCellStyleTop(aCellPos.mnCol, aCellPos.mnRow, impGetLineStyle(rTableLayouter, aCellPos.mnCol, aCellPos.mnRow, true, nColCount, nRowCount, bIsRTL));
264  aArray.SetCellStyleBottom(aCellPos.mnCol, aCellPos.mnRow, impGetLineStyle(rTableLayouter, aCellPos.mnCol, aCellPos.mnRow + 1, true, nColCount, nRowCount, bIsRTL));
265 
266  // ignore merged cells (all except the top-left of a merged cell)
267  if(!xCurrentCell->isMerged())
268  {
269  // check if we are the top-left of a merged cell
270  const sal_Int32 nXSpan(xCurrentCell->getColumnSpan());
271  const sal_Int32 nYSpan(xCurrentCell->getRowSpan());
272 
273  if(nXSpan > 1 || nYSpan > 1)
274  {
275  // if merged, set so at CellBorderArray for primitive creation
276  aArray.SetMergedRange(aCellPos.mnCol, aCellPos.mnRow, aCellPos.mnCol + nXSpan - 1, aCellPos.mnRow + nYSpan - 1);
277  }
278  }
279  }
280 
281  if(xCurrentCell.is() && !xCurrentCell->isMerged())
282  {
283  if(rTableLayouter.getCellArea(xCurrentCell, aCellPos, aCellArea))
284  {
285  // create cell transformation matrix
286  basegfx::B2DHomMatrix aCellMatrix;
287  aCellMatrix.set(0, 0, static_cast<double>(aCellArea.getWidth()));
288  aCellMatrix.set(1, 1, static_cast<double>(aCellArea.getHeight()));
289  aCellMatrix.set(0, 2, static_cast<double>(aCellArea.getMinX()) + aObjectRange.getMinX());
290  aCellMatrix.set(1, 2, static_cast<double>(aCellArea.getMinY()) + aObjectRange.getMinY());
291 
292  // handle cell fillings and text
293  const SfxItemSet& rCellItemSet = xCurrentCell->GetItemSet();
294  const sal_uInt32 nTextIndex(nColCount * aCellPos.mnRow + aCellPos.mnCol);
295  const SdrText* pSdrText = rTableObj.getText(nTextIndex);
297 
298  if(pSdrText)
299  {
300  // #i101508# take cell's local text frame distances into account
301  const sal_Int32 nLeft(xCurrentCell->GetTextLeftDistance());
302  const sal_Int32 nRight(xCurrentCell->GetTextRightDistance());
303  const sal_Int32 nUpper(xCurrentCell->GetTextUpperDistance());
304  const sal_Int32 nLower(xCurrentCell->GetTextLowerDistance());
305 
307  rCellItemSet,
308  pSdrText,
309  &nLeft,
310  &nUpper,
311  &nRight,
312  &nLower);
313  }
314  else
315  {
317  rCellItemSet,
318  pSdrText);
319  }
320 
321  // always create cell primitives for BoundRect and HitTest
322  {
324  new drawinglayer::primitive2d::SdrCellPrimitive2D(
325  aCellMatrix, aAttribute));
326  aRetval.append(xCellReference);
327  }
328 
329  // Create cell primitive without text.
330  aAttribute
332  rCellItemSet, nullptr);
333  rtl::Reference pCellReference
334  = new drawinglayer::primitive2d::SdrCellPrimitive2D(
335  aCellMatrix, aAttribute);
336 
337  sal_uInt16 nTransparence(
338  rCellItemSet.Get(XATTR_FILLTRANSPARENCE).GetValue());
339  if (nTransparence != 0)
340  {
341  pCellReference->setTransparenceForShadow(nTransparence);
342  }
343 
345  xCellReference(pCellReference);
346  aRetvalForShadow.append(xCellReference);
347  }
348  }
349  }
350  }
351 
352  // now create all CellBorderPrimitives
353  drawinglayer::primitive2d::Primitive2DContainer aCellBorderPrimitives(aArray.CreateB2DPrimitiveArray());
354 
355  if(!aCellBorderPrimitives.empty())
356  {
357  // this is already scaled (due to Table in non-uniform coordinates), so
358  // first transform removing scale
359  basegfx::B2DHomMatrix aTransform(
361  1.0 / aObjectRange.getWidth(),
362  1.0 / aObjectRange.getHeight()));
363 
364  // If RTL, mirror the whole unified table in X and move right.
365  // This is much easier than taking this into account for the whole
366  // index calculations
367  if(bIsRTL)
368  {
369  aTransform.scale(-1.0, 1.0);
370  aTransform.translate(1.0, 0.0);
371  }
372 
373  // create object matrix
374  const GeoStat& rGeoStat(rTableObj.GetGeoStat());
375  const double fShearX(-rGeoStat.mfTanShearAngle);
376  const double fRotate(rGeoStat.nRotationAngle ? (36000 - rGeoStat.nRotationAngle.get()) * F_PI18000 : 0.0);
378  aObjectRange.getWidth(), aObjectRange.getHeight(), fShearX, fRotate,
379  aObjectRange.getMinX(), aObjectRange.getMinY()));
380 
381  // add object matrix to transform. By doing so theoretically
382  // CellBorders could be also rotated/sheared for the first time ever.
383  // To completely make that work, the primitives already created in
384  // aRetval would also have to be based on ObjectMatrix, not only on
385  // ObjectRange as it currently is.
386  aTransform *= aObjectMatrix;
387 
388  // create a transform primitive with this and embed CellBorders
389  // and append to retval
390  aRetval.append(
392  aTransform,
393  drawinglayer::primitive2d::Primitive2DContainer(aCellBorderPrimitives)));
394 
395  // Borders are always the same for shadow as well.
397  aTransform, std::move(aCellBorderPrimitives)));
398  }
399  }
400 
401  if(!aRetval.empty())
402  {
403  // check and create evtl. shadow for created content
404  const SfxItemSet& rObjectItemSet = rTableObj.GetMergedItemSet();
405  const drawinglayer::attribute::SdrShadowAttribute aNewShadowAttribute(
407 
408  if(!aNewShadowAttribute.isDefault())
409  {
410  bool bDirectShadow
411  = rObjectItemSet.Get(SDRATTR_SHADOW, /*bSrchInParent=*/false)
412  .GetValue();
413  if (bDirectShadow)
414  {
415  // Shadow as direct formatting: no shadow for text, to be compatible
416  // with PowerPoint.
417  basegfx::B2DHomMatrix aMatrix;
419  std::move(aRetval), aNewShadowAttribute, aMatrix, &aRetvalForShadow);
420  }
421  else
422  {
423  // Shadow as style: shadow for text, to be backwards-compatible.
425  std::move(aRetval), aNewShadowAttribute);
426  }
427  }
428  }
429 
430  return aRetval;
431  }
432  else
433  {
434  // take unrotated snap rect (direct model data) for position and size
436 
437  // create object matrix
438  const GeoStat& rGeoStat(rTableObj.GetGeoStat());
439  const double fShearX(-rGeoStat.mfTanShearAngle);
440  const double fRotate(rGeoStat.nRotationAngle ? (36000 - rGeoStat.nRotationAngle.get()) * F_PI18000 : 0.0);
442  aObjectRange.getWidth(), aObjectRange.getHeight(), fShearX, fRotate,
443  aObjectRange.getMinX(), aObjectRange.getMinY()));
444 
445  // created an invisible outline for the cases where no visible content exists
448  aObjectMatrix));
449 
451  }
452  }
453 
454  ViewContactOfTableObj::ViewContactOfTableObj(sdr::table::SdrTableObj& rTableObj)
455  : ViewContactOfSdrObj(rTableObj)
456  {
457  }
458 
460  {
461  }
462 } // end of namespace
463 
464 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
css::uno::Reference< css::table::XTable > getTable() const
Definition: svdotable.cxx:937
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()
constexpr Point convert(const Point &rPoint, o3tl::Length eFrom, o3tl::Length eTo)
sal_Int32 getMinY() const
#define PRIMITIVE2D_ID_SDRCELLPRIMITIVE2D
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:1344
double getWidth() const
Primitive2DContainer createEmbeddedShadowPrimitive(Primitive2DContainer &&rContent, const attribute::SdrShadowAttribute &rShadow, const basegfx::B2DHomMatrix &rObjectMatrix, const Primitive2DContainer *pContentForShadow)
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:214
attribute::SdrFillTextAttribute maSdrFTAttribute
const GeoStat & GetGeoStat() const
Definition: svdotext.hxx:392
constexpr TypedWhichId< SdrOnOffItem > SDRATTR_SHADOW(SDRATTR_SHADOW_FIRST+0)
basegfx::B2DHomMatrix maTransform
constexpr TypedWhichId< XFillTransparenceItem > XATTR_FILLTRANSPARENCE(XATTR_FILL_FIRST+5)
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:2024
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:1978
Primitive2DReference createHiddenGeometryPrimitives2D(const basegfx::B2DHomMatrix &rMatrix)
#define F_PI18000
Contains the widths of primary and secondary line of a frame style.
Definition: framelink.hxx:98
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:1435
const TableLayouter & getTableLayouter() const
Definition: svdotable.cxx:1415
Primitive2DReference createPolyPolygonFillPrimitive(const basegfx::B2DPolyPolygon &rPolyPolygon, const attribute::SdrFillAttribute &rFill, const attribute::FillGradientAttribute &rFillGradient)