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