1 /* This file is part of the KDE project
2    Copyright 2006 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
3 
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Library General Public
6    License as published by the Free Software Foundation; either
7    version 2 of the License, or (at your option) any later version.
8 
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Library General Public License for more details.
13 
14    You should have received a copy of the GNU Library General Public License
15    along with this library; see the file COPYING.LIB.  If not, write to
16    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17    Boston, MA 02110-1301, USA.
18 */
19 
20 // Local
21 #include "TableShape.h"
22 
23 #include "TablePageManager.h"
24 
25 #include <QPainter>
26 
27 #include <KoShapeContainer.h>
28 #include <KoXmlNS.h>
29 
30 #include <SheetsDebug.h>
31 #include <CellView.h>
32 #include <Damages.h>
33 #include <Condition.h>
34 #include <Map.h>
35 #include <PrintSettings.h>
36 #include <Region.h>
37 #include <RowColumnFormat.h>
38 #include <RowFormatStorage.h>
39 #include <Sheet.h>
40 #include <SheetView.h>
41 #include <Value.h>
42 #include <odf/SheetsOdf.h>
43 
44 using namespace Calligra::Sheets;
45 
46 class TableShape::Private
47 {
48 public:
49     int         columns;
50     int         rows;
51     SheetView*  sheetView;
52     bool        isMaster;
53     TablePageManager* pageManager;
54 
55 public:
56     void adjustColumnDimensions(Sheet* sheet, double factor);
57     void adjustRowDimensions(Sheet* sheet, double factor);
58 };
59 
adjustColumnDimensions(Sheet * sheet,double factor)60 void TableShape::Private::adjustColumnDimensions(Sheet* sheet, double factor)
61 {
62     for (int col = 1; col <= columns; ++col) {
63         ColumnFormat* const columnFormat = sheet->nonDefaultColumnFormat(col);
64         columnFormat->setWidth(columnFormat->width() * factor);
65     }
66 }
67 
adjustRowDimensions(Sheet * sheet,double factor)68 void TableShape::Private::adjustRowDimensions(Sheet* sheet, double factor)
69 {
70     for (int row = 1; row <= rows; ++row) {
71         sheet->rowFormats()->setRowHeight(row, row, sheet->rowFormats()->rowHeight(row) * factor);
72     }
73 }
74 
75 
76 
TableShape(int columns,int rows)77 TableShape::TableShape(int columns, int rows)
78         : d(new Private)
79 {
80     setObjectName(QLatin1String("TableShape"));
81     d->columns = columns;
82     d->rows = rows;
83     d->sheetView = 0;
84     d->isMaster = false;
85     d->pageManager = 0;
86 }
87 
~TableShape()88 TableShape::~TableShape()
89 {
90     delete d->pageManager;
91     delete d->sheetView;
92     if (KoShape::userData()) {
93         map()->removeSheet(qobject_cast<Sheet*>(KoShape::userData())); // declare the sheet as deleted
94     }
95     delete d;
96 }
97 
columns() const98 int TableShape::columns() const
99 {
100     return d->columns;
101 }
102 
rows() const103 int TableShape::rows() const
104 {
105     return d->rows;
106 }
107 
setColumns(int columns)108 void TableShape::setColumns(int columns)
109 {
110     Q_ASSERT(columns > 0);
111     if(!sheet())
112         return;
113     const double factor = (double) d->columns / columns;
114     d->columns = columns;
115     d->adjustColumnDimensions(qobject_cast<Sheet*>(KoShape::userData()), factor);
116     setVisibleCellRange(QRect(1, 1, d->columns, d->rows));
117     d->sheetView->invalidate();
118     if (!d->pageManager) {
119         return;
120     }
121     PrintSettings settings = *sheet()->printSettings();
122     settings.setPrintRegion(Region(1, 1, d->columns, d->rows, sheet()));
123     d->pageManager->setPrintSettings(settings);
124 }
125 
setRows(int rows)126 void TableShape::setRows(int rows)
127 {
128     Q_ASSERT(rows > 0);
129     if(!sheet())
130         return;
131     const double factor = (double) d->rows / rows;
132     d->rows = rows;
133     d->adjustRowDimensions(qobject_cast<Sheet*>(KoShape::userData()), factor);
134     setVisibleCellRange(QRect(1, 1, d->columns, d->rows));
135     d->sheetView->invalidate();
136     if (!d->pageManager) {
137         return;
138     }
139     PrintSettings settings = *sheet()->printSettings();
140     settings.setPrintRegion(Region(1, 1, d->columns, d->rows, sheet()));
141     d->pageManager->setPrintSettings(settings);
142 }
143 
paint(QPainter & painter,const KoViewConverter & converter,KoShapePaintingContext &)144 void TableShape::paint(QPainter& painter, const KoViewConverter& converter, KoShapePaintingContext &)
145 {
146 #ifndef NDEBUG
147     if (KoShape::parent()) {
148         debugSheets << KoShape::parent()->name() <<  KoShape::parent()->shapeId() << KoShape::parent()->boundingRect();
149     }
150 #endif
151     const QRectF paintRect = QRectF(QPointF(0.0, 0.0), size());
152 
153     applyConversion(painter, converter);
154     painter.setClipRect(paintRect, Qt::IntersectClip);
155 
156     // painting cell contents
157     d->sheetView->setViewConverter(&converter);
158     d->sheetView->paintCells(painter, paintRect, QPointF(0.0, 0.0));
159 }
160 
loadOdf(const KoXmlElement & element,KoShapeLoadingContext & context)161 bool TableShape::loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context)
162 {
163     //debugSheets << "LOADING TABLE SHAPE";
164     if (sheet() && element.namespaceURI() == KoXmlNS::table && element.localName() == "table") {
165         if (!Odf::loadTableShape(sheet(), element, context)) return false;
166 
167         const QRect usedArea = sheet()->usedArea();
168         d->columns = usedArea.width();
169         d->rows = usedArea.height();
170 
171         QSizeF size(0.0, 0.0);
172         for (int col = 1; col <= d->columns; ++col) {
173             size.rwidth() += sheet()->columnFormat(col)->visibleWidth();
174         }
175         size.rheight() = sheet()->rowFormats()->totalVisibleRowHeight(1, d->rows);
176         KoShape::setSize(size);
177         return true;
178     }
179     return false;
180 }
181 
saveOdf(KoShapeSavingContext & context) const182 void TableShape::saveOdf(KoShapeSavingContext & context) const
183 {
184     if (!sheet())
185         return;
186     Odf::saveTableShape(sheet(), context);
187 }
188 
setMap(Map * map)189 void TableShape::setMap(Map *map)
190 {
191     if (map == 0)
192         return;
193     Sheet* const sheet = map->addNewSheet();
194     d->sheetView = new SheetView(sheet);
195     KoShape::setUserData(sheet);
196     d->isMaster = true;
197     setVisibleCellRange(QRect(1, 1, d->columns, d->rows));
198 
199     connect(map, SIGNAL(damagesFlushed(QList<Damage*>)),
200             this, SLOT(handleDamages(QList<Damage*>)));
201 
202     // Initialize the size using the default column/row dimensions.
203     QSize size;
204     for (int col = 1; col <= d->columns; ++col) {
205         size.rwidth() += sheet->columnFormat(col)->visibleWidth();
206     }
207     size.rheight() = sheet->rowFormats()->totalVisibleRowHeight(1, d->rows);
208     KoShape::setSize(size);
209 }
210 
setSize(const QSizeF & newSize)211 void TableShape::setSize(const QSizeF& newSize)
212 {
213     const QSizeF oldSize = size();
214     if (oldSize == newSize)
215         return;
216 
217     QSizeF size2 = oldSize;
218     const qreal cellWidth = map()->defaultColumnFormat()->width();
219     const qreal cellHeight = map()->defaultRowFormat()->height();
220 
221     // Note that the following four variables can also be negative
222     const qreal dx = newSize.width() - oldSize.width();
223     const qreal dy = newSize.height() - oldSize.height();
224     int numAddedCols = 0;
225     int numAddedRows = 0;
226 
227     if (qAbs(dx) >= cellWidth) {
228         numAddedCols = int(dx / cellWidth);
229         size2.rwidth() += cellWidth * numAddedCols;
230     }
231     if (qAbs(dy) >= cellHeight) {
232         numAddedRows = int(dy / cellHeight);
233         size2.rheight() += cellHeight * numAddedRows;
234     }
235     if (qAbs(dx) >= cellWidth || qAbs(dy) >= cellHeight) {
236         d->columns += numAddedCols;
237         d->rows += numAddedRows;
238         setVisibleCellRange(QRect(1, 1, d->columns, d->rows));
239         d->sheetView->invalidate();
240         KoShape::setSize(size2);
241     }
242 }
243 
map() const244 Map* TableShape::map() const
245 {
246     return qobject_cast<Sheet*>(KoShape::userData())->map();
247 }
248 
sheet() const249 Sheet* TableShape::sheet() const
250 {
251     return qobject_cast<Sheet*>(KoShape::userData());
252 }
253 
sheetView() const254 SheetView* TableShape::sheetView() const
255 {
256     return d->sheetView;
257 }
258 
setSheet(const QString & sheetName)259 void TableShape::setSheet(const QString& sheetName)
260 {
261     Sheet* const sheet = map()->findSheet(sheetName);
262     if (! sheet)
263         return;
264     delete d->sheetView;
265     d->sheetView = new SheetView(sheet);
266     KoShape::setUserData(sheet);
267     setColumns(d->columns);
268     setRows(d->rows);
269     setVisibleCellRange(QRect(1, 1, d->columns, d->rows));
270     update();
271 }
272 
setVisibleCellRange(const QRect & cellRange)273 void TableShape::setVisibleCellRange(const QRect& cellRange)
274 {
275     Q_ASSERT(KoShape::userData());
276     if (!d->sheetView) {
277         d->sheetView = new SheetView(sheet());
278     }
279     d->sheetView->setPaintCellRange(cellRange & QRect(1, 1, d->columns, d->rows));
280 }
281 
shapeChanged(ChangeType type,KoShape * shape)282 void TableShape::shapeChanged(ChangeType type, KoShape *shape)
283 {
284     Q_UNUSED(shape);
285     // If this is a master table shape, the parent changed and we have no parent yet...
286     if (d->isMaster && type == ParentChanged && !d->pageManager) {
287         d->pageManager = new TablePageManager(this);
288         return;
289     }
290     // Not the master table shape? Not embedded into a container?
291     if (!d->isMaster || !KoShape::parent()) {
292         return;
293     }
294     // Not the changes, we want to react on?
295     if (type != SizeChanged) {
296         return;
297     }
298     d->pageManager->layoutPages();
299 }
300 
handleDamages(const QList<Damage * > & damages)301 void TableShape::handleDamages(const QList<Damage*>& damages)
302 {
303     QList<Damage*>::ConstIterator end(damages.end());
304     for (QList<Damage*>::ConstIterator it = damages.begin(); it != end; ++it) {
305         Damage* damage = *it;
306         if (!damage) continue;
307 
308         if (damage->type() == Damage::Cell) {
309             CellDamage* cellDamage = static_cast<CellDamage*>(damage);
310             const Region region = cellDamage->region();
311 
312             if (cellDamage->changes() & CellDamage::Appearance)
313                 d->sheetView->invalidateRegion(region);
314             continue;
315         }
316 
317         if (damage->type() == Damage::Sheet) {
318             SheetDamage* sheetDamage = static_cast<SheetDamage*>(damage);
319 
320             if (sheetDamage->changes() & SheetDamage::PropertiesChanged)
321                 d->sheetView->invalidate();
322             continue;
323         }
324     }
325 
326     update();
327 }
328