1 | /* This file is part of the KDE project |
2 | Copyright (C) 1998, 1999 Torben Weis <weis@kde.org> |
3 | Copyright (C) 2000 - 2005 The KSpread Team <calligra-devel@kde.org> |
4 | |
5 | This library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Library General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2 of the License, or (at your option) any later version. |
9 | |
10 | This library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Library General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Library General Public License |
16 | along with this library; see the file COPYING.LIB. If not, write to |
17 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
18 | Boston, MA 02110-1301, USA. |
19 | */ |
20 | |
21 | // Local |
22 | #include "RowColumnFormat.h" |
23 | |
24 | #include <float.h> |
25 | #include <iostream> |
26 | #include <stdlib.h> |
27 | #include <stdio.h> |
28 | |
29 | #include <kdebug.h> |
30 | #include <klocale.h> |
31 | |
32 | #include <KoXmlNS.h> |
33 | #include <KoGenStyles.h> |
34 | #include <KoGlobal.h> |
35 | #include <KoStyleStack.h> |
36 | #include <KoOdfLoadingContext.h> |
37 | #include <KoOdfStylesReader.h> |
38 | |
39 | #include "CellStorage.h" |
40 | #include "Global.h" |
41 | #include "Map.h" |
42 | #include "Region.h" |
43 | #include "RowFormatStorage.h" |
44 | #include "Sheet.h" |
45 | #include "SheetPrint.h" |
46 | #include "Style.h" |
47 | #include "StyleManager.h" |
48 | |
49 | using namespace std; |
50 | using namespace Calligra::Sheets; |
51 | |
52 | /***************************************************************************** |
53 | * |
54 | * RowFormat |
55 | * |
56 | *****************************************************************************/ |
57 | |
58 | class RowFormat::Private |
59 | { |
60 | public: |
61 | Sheet* sheet; |
62 | RowFormat* next; |
63 | RowFormat* prev; |
64 | double height; |
65 | int row; |
66 | bool hide : 1; |
67 | bool filtered : 1; |
68 | bool pageBreak : 1; // before row |
69 | }; |
70 | |
71 | RowFormat::RowFormat() |
72 | : d(new Private) |
73 | { |
74 | d->sheet = 0; |
75 | d->row = 0; |
76 | d->height = 0.0; |
77 | d->hide = false; |
78 | d->filtered = false; |
79 | d->pageBreak = false; |
80 | d->next = 0; |
81 | d->prev = 0; |
82 | } |
83 | |
84 | RowFormat::RowFormat(const RowFormat& other) |
85 | : d(new Private(*other.d)) |
86 | { |
87 | } |
88 | |
89 | RowFormat::RowFormat(const RowFormatStorage *rows, int row) |
90 | : d(new Private) |
91 | { |
92 | d->sheet = rows->sheet(); |
93 | d->row = row; |
94 | d->height = rows->rowHeight(row); |
95 | d->hide = rows->isHidden(row); |
96 | d->filtered = rows->isFiltered(row); |
97 | d->pageBreak = rows->hasPageBreak(row); |
98 | d->next = d->prev = 0; |
99 | } |
100 | |
101 | RowFormat::~RowFormat() |
102 | { |
103 | if (d->next) |
104 | d->next->setPrevious(d->prev); |
105 | if (d->prev) |
106 | d->prev->setNext(d->next); |
107 | delete d; |
108 | } |
109 | |
110 | void RowFormat::setSheet(Sheet* sheet) |
111 | { |
112 | d->sheet = sheet; |
113 | } |
114 | |
115 | void RowFormat::setHeight(double height) |
116 | { |
117 | // avoid unnecessary updates |
118 | if (qAbs(height - this->height()) < DBL_EPSILON) |
119 | return; |
120 | |
121 | // default RowFormat? |
122 | if (!d->sheet) { |
123 | d->height = height; |
124 | return; |
125 | } |
126 | |
127 | // Raise document height by new height and lower it by old height. |
128 | if (!isHidden() && !isFiltered()) |
129 | d->sheet->adjustDocumentHeight(height - d->height); |
130 | |
131 | d->height = height; |
132 | |
133 | d->sheet->print()->updateVerticalPageParameters(row()); |
134 | } |
135 | |
136 | double RowFormat::height() const |
137 | { |
138 | return d->height; |
139 | } |
140 | |
141 | double RowFormat::visibleHeight() const |
142 | { |
143 | if (d->hide || d->filtered) |
144 | return 0.0; |
145 | return d->height; |
146 | } |
147 | |
148 | QDomElement RowFormat::save(QDomDocument& doc, int yshift) const |
149 | { |
150 | Q_ASSERT(d->sheet); |
151 | QDomElement row = doc.createElement("row" ); |
152 | row.setAttribute("height" , d->height); |
153 | row.setAttribute("row" , d->row - yshift); |
154 | if (d->hide) |
155 | row.setAttribute("hide" , (int) d->hide); |
156 | |
157 | const Style style = d->sheet->cellStorage()->style(QRect(1, d->row, KS_colMax, 1)); |
158 | if (!style.isEmpty()) { |
159 | kDebug(36003) << "saving cell style of row" << d->row; |
160 | QDomElement format; |
161 | style.saveXML(doc, format, d->sheet->map()->styleManager()); |
162 | row.appendChild(format); |
163 | } |
164 | |
165 | return row; |
166 | } |
167 | |
168 | bool RowFormat::load(const KoXmlElement & row, int yshift, Paste::Mode mode) |
169 | { |
170 | Q_ASSERT(d->sheet); |
171 | bool ok; |
172 | |
173 | d->row = row.attribute("row" ).toInt(&ok) + yshift; |
174 | if (!ok) |
175 | return false; |
176 | |
177 | if (row.hasAttribute("height" )) { |
178 | if (d->sheet->map()->syntaxVersion() < 1) //compatibility with old format - was in millimeter |
179 | d->height = qRound(MM_TO_POINT(row.attribute("height" ).toDouble(&ok))); |
180 | else |
181 | d->height = row.attribute("height" ).toDouble(&ok); |
182 | |
183 | if (!ok) return false; |
184 | } |
185 | |
186 | // Validation |
187 | if (d->height < 0) { |
188 | kDebug(36001) << "Value height=" << d->height << " out of range" ; |
189 | return false; |
190 | } |
191 | if (d->row < 1 || d->row > KS_rowMax) { |
192 | kDebug(36001) << "Value row=" << d->row << " out of range" ; |
193 | return false; |
194 | } |
195 | |
196 | if (row.hasAttribute("hide" )) { |
197 | setHidden((int) row.attribute("hide" ).toInt(&ok)); |
198 | if (!ok) |
199 | return false; |
200 | } |
201 | |
202 | KoXmlElement f(row.namedItem("format" ).toElement()); |
203 | |
204 | if (!f.isNull() && (mode == Paste::Normal || mode == Paste::Format || mode == Paste::NoBorder)) { |
205 | Style style; |
206 | if (!style.loadXML(f, mode)) |
207 | return false; |
208 | d->sheet->cellStorage()->setStyle(Region(QRect(1, d->row, KS_colMax, 1)), style); |
209 | return true; |
210 | } |
211 | |
212 | return true; |
213 | } |
214 | |
215 | int RowFormat::row() const |
216 | { |
217 | return d->row; |
218 | } |
219 | |
220 | void RowFormat::setRow(int row) |
221 | { |
222 | d->row = row; |
223 | } |
224 | |
225 | RowFormat* RowFormat::next() const |
226 | { |
227 | return d->next; |
228 | } |
229 | |
230 | RowFormat* RowFormat::previous() const |
231 | { |
232 | return d->prev; |
233 | } |
234 | |
235 | void RowFormat::setNext(RowFormat* next) |
236 | { |
237 | d->next = next; |
238 | } |
239 | |
240 | void RowFormat::setPrevious(RowFormat* prev) |
241 | { |
242 | d->prev = prev; |
243 | } |
244 | |
245 | void RowFormat::setHidden(bool _hide, bool repaint) |
246 | { |
247 | Q_UNUSED(repaint); |
248 | Q_ASSERT(d->sheet); |
249 | if (_hide != d->hide) { // only if we change the status |
250 | if (_hide) { |
251 | // Lower maximum size by height of row |
252 | d->sheet->adjustDocumentHeight(- height()); |
253 | d->hide = _hide; //hide must be set after we requested the height |
254 | } else { |
255 | // Rise maximum size by height of row |
256 | d->hide = _hide; //unhide must be set before we request the height |
257 | d->sheet->adjustDocumentHeight(height()); |
258 | } |
259 | } |
260 | } |
261 | |
262 | bool RowFormat::isHidden() const |
263 | { |
264 | return d->hide; |
265 | } |
266 | |
267 | void RowFormat::setFiltered(bool filtered) |
268 | { |
269 | d->filtered = filtered; |
270 | } |
271 | |
272 | bool RowFormat::isFiltered() const |
273 | { |
274 | return d->filtered; |
275 | } |
276 | |
277 | bool RowFormat::isHiddenOrFiltered() const |
278 | { |
279 | return d->hide || d->filtered; |
280 | } |
281 | |
282 | bool RowFormat::isDefault() const |
283 | { |
284 | return !d->sheet; |
285 | } |
286 | |
287 | void RowFormat::setPageBreak(bool enable) |
288 | { |
289 | d->pageBreak = enable; |
290 | } |
291 | |
292 | bool RowFormat::hasPageBreak() const |
293 | { |
294 | return d->pageBreak; |
295 | } |
296 | |
297 | bool RowFormat::operator==(const RowFormat& other) const |
298 | { |
299 | // NOTE Stefan: Don't compare sheet and cell. |
300 | if (d->height != other.d->height) |
301 | return false; |
302 | if (d->hide != other.d->hide) |
303 | return false; |
304 | if (d->filtered != other.d->filtered) |
305 | return false; |
306 | if (d->pageBreak != other.d->pageBreak) { |
307 | return false; |
308 | } |
309 | return true; |
310 | } |
311 | |
312 | |
313 | /***************************************************************************** |
314 | * |
315 | * ColumnFormat |
316 | * |
317 | *****************************************************************************/ |
318 | |
319 | class ColumnFormat::Private |
320 | { |
321 | public: |
322 | Sheet* sheet; |
323 | ColumnFormat* next; |
324 | ColumnFormat* prev; |
325 | double width; |
326 | int column; |
327 | bool hide : 1; |
328 | bool filtered : 1; |
329 | bool pageBreak : 1; // before column |
330 | }; |
331 | |
332 | ColumnFormat::ColumnFormat() |
333 | : d(new Private) |
334 | { |
335 | d->sheet = 0; |
336 | d->column = 0; |
337 | d->width = 0.0; |
338 | d->hide = false; |
339 | d->filtered = false; |
340 | d->pageBreak = false; |
341 | d->next = 0; |
342 | d->prev = 0; |
343 | } |
344 | |
345 | ColumnFormat::ColumnFormat(const ColumnFormat& other) |
346 | : d(new Private(*other.d)) |
347 | { |
348 | } |
349 | |
350 | ColumnFormat::~ColumnFormat() |
351 | { |
352 | if (d->next) |
353 | d->next->setPrevious(d->prev); |
354 | if (d->prev) |
355 | d->prev->setNext(d->next); |
356 | delete d; |
357 | } |
358 | |
359 | void ColumnFormat::setSheet(Sheet* sheet) |
360 | { |
361 | d->sheet = sheet; |
362 | } |
363 | |
364 | void ColumnFormat::setWidth(double width) |
365 | { |
366 | // avoid unnecessary updates |
367 | if (qAbs(width - this->width()) < DBL_EPSILON) |
368 | return; |
369 | |
370 | // default ColumnFormat? |
371 | if (!d->sheet) { |
372 | d->width = width; |
373 | return; |
374 | } |
375 | |
376 | // Raise document width by new width and lower it by old width. |
377 | if (!isHidden() && !isFiltered()) |
378 | d->sheet->adjustDocumentWidth(width - d->width); |
379 | |
380 | d->width = width; |
381 | |
382 | d->sheet->print()->updateHorizontalPageParameters(column()); |
383 | } |
384 | |
385 | double ColumnFormat::width() const |
386 | { |
387 | return d->width; |
388 | } |
389 | |
390 | double ColumnFormat::visibleWidth() const |
391 | { |
392 | if (d->hide || d->filtered) |
393 | return 0.0; |
394 | return d->width; |
395 | } |
396 | |
397 | QDomElement ColumnFormat::save(QDomDocument& doc, int xshift) const |
398 | { |
399 | Q_ASSERT(d->sheet); |
400 | QDomElement col(doc.createElement("column" )); |
401 | col.setAttribute("width" , d->width); |
402 | col.setAttribute("column" , d->column - xshift); |
403 | |
404 | if (d->hide) |
405 | col.setAttribute("hide" , (int) d->hide); |
406 | |
407 | const Style style = d->sheet->cellStorage()->style(QRect(d->column, 1, 1, KS_rowMax)); |
408 | if (!style.isEmpty()) { |
409 | kDebug(36003) << "saving cell style of column" << d->column; |
410 | QDomElement format(doc.createElement("format" )); |
411 | style.saveXML(doc, format, d->sheet->map()->styleManager()); |
412 | col.appendChild(format); |
413 | } |
414 | |
415 | return col; |
416 | } |
417 | |
418 | bool ColumnFormat::load(const KoXmlElement & col, int xshift, Paste::Mode mode) |
419 | { |
420 | Q_ASSERT(d->sheet); |
421 | bool ok; |
422 | if (col.hasAttribute("width" )) { |
423 | if (d->sheet->map()->syntaxVersion() < 1) //combatibility to old format - was in millimeter |
424 | d->width = qRound(MM_TO_POINT(col.attribute("width" ).toDouble(&ok))); |
425 | else |
426 | d->width = col.attribute("width" ).toDouble(&ok); |
427 | |
428 | if (!ok) |
429 | return false; |
430 | } |
431 | |
432 | d->column = col.attribute("column" ).toInt(&ok) + xshift; |
433 | |
434 | if (!ok) |
435 | return false; |
436 | |
437 | // Validation |
438 | if (d->width < 0) { |
439 | kDebug(36001) << "Value width=" << d->width << " out of range" ; |
440 | return false; |
441 | } |
442 | if (d->column < 1 || d->column > KS_colMax) { |
443 | kDebug(36001) << "Value col=" << d->column << " out of range" ; |
444 | return false; |
445 | } |
446 | if (col.hasAttribute("hide" )) { |
447 | setHidden((int) col.attribute("hide" ).toInt(&ok)); |
448 | if (!ok) |
449 | return false; |
450 | } |
451 | |
452 | KoXmlElement f(col.namedItem("format" ).toElement()); |
453 | |
454 | if (!f.isNull() && (mode == Paste::Normal || mode == Paste::Format || mode == Paste::NoBorder)) { |
455 | Style style; |
456 | if (!style.loadXML(f, mode)) |
457 | return false; |
458 | d->sheet->cellStorage()->setStyle(Region(QRect(d->column, 1, 1, KS_rowMax)), style); |
459 | return true; |
460 | } |
461 | |
462 | return true; |
463 | } |
464 | |
465 | int ColumnFormat::column() const |
466 | { |
467 | return d->column; |
468 | } |
469 | |
470 | void ColumnFormat::setColumn(int column) |
471 | { |
472 | d->column = column; |
473 | } |
474 | |
475 | ColumnFormat* ColumnFormat::next() const |
476 | { |
477 | return d->next; |
478 | } |
479 | |
480 | ColumnFormat* ColumnFormat::previous() const |
481 | { |
482 | return d->prev; |
483 | } |
484 | |
485 | void ColumnFormat::setNext(ColumnFormat* next) |
486 | { |
487 | d->next = next; |
488 | } |
489 | |
490 | void ColumnFormat::setPrevious(ColumnFormat* prev) |
491 | { |
492 | d->prev = prev; |
493 | } |
494 | |
495 | void ColumnFormat::setHidden(bool _hide) |
496 | { |
497 | Q_ASSERT(d->sheet); |
498 | if (_hide != d->hide) { // only if we change the status |
499 | if (_hide) { |
500 | // Lower maximum size by width of column |
501 | d->sheet->adjustDocumentWidth(- width()); |
502 | d->hide = _hide; //hide must be set after we requested the width |
503 | } else { |
504 | // Rise maximum size by width of column |
505 | d->hide = _hide; //unhide must be set before we request the width |
506 | d->sheet->adjustDocumentWidth(width()); |
507 | } |
508 | } |
509 | } |
510 | |
511 | bool ColumnFormat::isHidden() const |
512 | { |
513 | return d->hide; |
514 | } |
515 | |
516 | void ColumnFormat::setFiltered(bool filtered) |
517 | { |
518 | d->filtered = filtered; |
519 | } |
520 | |
521 | bool ColumnFormat::isFiltered() const |
522 | { |
523 | return d->filtered; |
524 | } |
525 | |
526 | bool ColumnFormat::isHiddenOrFiltered() const |
527 | { |
528 | return d->hide || d->filtered; |
529 | } |
530 | |
531 | bool ColumnFormat::isDefault() const |
532 | { |
533 | return !d->sheet; |
534 | } |
535 | |
536 | void ColumnFormat::setPageBreak(bool enable) |
537 | { |
538 | d->pageBreak = enable; |
539 | } |
540 | |
541 | bool ColumnFormat::hasPageBreak() const |
542 | { |
543 | return d->pageBreak; |
544 | } |
545 | |
546 | bool ColumnFormat::operator==(const ColumnFormat& other) const |
547 | { |
548 | // NOTE Stefan: Don't compare sheet and cell. |
549 | if (d->width != other.d->width) |
550 | return false; |
551 | if (d->hide != other.d->hide) |
552 | return false; |
553 | if (d->filtered != other.d->filtered) |
554 | return false; |
555 | if (d->pageBreak != other.d->pageBreak) { |
556 | return false; |
557 | } |
558 | return true; |
559 | } |
560 | |