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