1 /* AbiSource
2 *
3 * Copyright (C) 2005 INdT
4 * Author: Daniel d'Andrada T. de Carvalho <daniel.carvalho@indt.org.br>
5 * Copyright 2009 AbiSource Corporation B.V.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 * 02110-1301 USA.
21 */
22
23 // Class definition include
24 #include "ODe_Table_Listener.h"
25
26 // Internal includes
27 #include "ODe_AuxiliaryData.h"
28 #include "ODe_Common.h"
29 #include "ODe_ListenerAction.h"
30 #include "ODe_AutomaticStyles.h"
31 #include "ODe_Style_Style.h"
32 #include "ODe_Text_Listener.h"
33
34 // AbiWord includes
35 #include <pp_AttrProp.h>
36 #include <gsf/gsf-output-memory.h>
37
38
39 /**
40 * Constructor
41 */
ODe_Table_Listener(ODe_Styles & rStyles,ODe_AutomaticStyles & rAutomatiStyles,GsfOutput * pTextOutput,ODe_AuxiliaryData & rAuxiliaryData,UT_uint8 zIndex,UT_uint8 spacesOffset)42 ODe_Table_Listener::ODe_Table_Listener(ODe_Styles& rStyles,
43 ODe_AutomaticStyles& rAutomatiStyles,
44 GsfOutput* pTextOutput,
45 ODe_AuxiliaryData& rAuxiliaryData,
46 UT_uint8 zIndex,
47 UT_uint8 spacesOffset)
48 :
49 ODe_AbiDocListenerImpl(spacesOffset),
50 m_pColumns(NULL),
51 m_numColumns(0),
52 m_pRows(NULL),
53 m_numRows(0),
54 m_pTextOutput(pTextOutput),
55 m_rStyles(rStyles),
56 m_rAutomatiStyles(rAutomatiStyles),
57 m_rAuxiliaryData(rAuxiliaryData),
58 m_zIndex(zIndex)
59 {
60 }
61
62
63 /**
64 * Destructor
65 */
~ODe_Table_Listener()66 ODe_Table_Listener::~ODe_Table_Listener() {
67 DELETEPV(m_pColumns);
68 DELETEPV(m_pRows);
69 UT_VECTOR_PURGEALL(ODe_Table_Cell*, m_cells);
70 UT_VECTOR_PURGEALL(UT_UTF8String*, columnStyleNames);
71 UT_VECTOR_PURGEALL(UT_UTF8String*, rowStyleNames);
72 }
73
74
75 /**
76 *
77 */
openTable(const PP_AttrProp * pAP,ODe_ListenerAction &)78 void ODe_Table_Listener::openTable(const PP_AttrProp* pAP,
79 ODe_ListenerAction& /*rAction*/) {
80 const gchar* pValue;
81 bool ok;
82 const gchar* pVar;
83 ODe_Style_Style* pStyle;
84 std::string buffer;
85 UT_UTF8String styleName;
86 UT_GenericVector< ODe_Style_Style*> vecStyles;
87 m_rAuxiliaryData.m_tableCount++;
88 UT_UTF8String_sprintf(m_tableName, "Table%u", m_rAuxiliaryData.m_tableCount);
89
90 if (ODe_Style_Style::hasTableStyleProps(pAP)) {
91 m_tableStyleName = m_tableName; // Plain simple
92
93 pStyle = m_rAutomatiStyles.addTableStyle(m_tableStyleName);
94 pStyle->fetchAttributesFromAbiTable(pAP);
95 pStyle = NULL; // We're done with it.
96 // OBS: There's no need to delete it as it will be done
97 // later by ODe_AutomaticStyles destructor.
98 }
99
100
101 // We don't have to check if there are any properties to export at all,
102 // because AbiWord has different default cell style properties than OpenDocument,
103 // which means we'll always have to export the styles. This will only result in
104 // writing out redundant properties when the user-selected style properties
105 // exactly match the default OpenDocument default properties; we will just ignore
106 // this case.
107 m_tableWideCellStyle.fetchAttributesFromAbiCell(pAP);
108
109 // NOTE: Don't use the number of table-column-props values to determine
110 // the number of columns in the table. There can be more values than there
111 // are actual columns; see bug 11863 for an example.
112 m_numColumns = 0;
113 UT_uint32 curColProp = 0;
114 ok = pAP->getProperty("table-column-props", pValue);
115 if (ok && pValue != NULL) {
116 pVar = pValue;
117 while (*pVar != 0) {
118 if (*pVar == '/') {
119 if (!buffer.empty()) {
120 curColProp++;
121 UT_UTF8String_sprintf(styleName, "%s.col%u",
122 m_tableName.utf8_str(), curColProp);
123
124 pStyle = m_rAutomatiStyles.addTableColumnStyle(styleName);
125 vecStyles.addItem(pStyle);
126 pStyle->setColumnWidth(buffer.c_str());
127
128 columnStyleNames.addItem(new UT_UTF8String(styleName));
129
130 buffer.clear();
131 } else {
132 columnStyleNames.addItem(new UT_UTF8String(""));
133 }
134 } else {
135 buffer += *pVar;
136 }
137 pVar++;
138 }
139 }
140
141 buffer.clear();
142
143 UT_sint32 cnt = 0;
144 ok = pAP->getProperty("table-rel-column-props", pValue);
145 if (ok && pValue != NULL)
146 {
147 pVar = pValue;
148 while (*pVar != 0)
149 {
150 if (*pVar == '/')
151 {
152 if (!buffer.empty())
153 {
154 if (cnt >= vecStyles.size())
155 {
156 UT_DEBUGMSG(("table-rel-column-props and table-column-props "
157 "have mismatched column counts! ignoring "
158 "further values.\n"));
159 break;
160 }
161 pStyle = vecStyles.getNthItem(cnt);
162 cnt++;
163 pStyle->setRelColumnWidth(buffer.c_str());
164 buffer.clear();
165 }
166 }
167 else
168 {
169 buffer += *pVar;
170 }
171 pVar++;
172 }
173 }
174
175 buffer.clear();
176
177 // NOTE: Don't use the number of table-row-height values to determine
178 // the number of rows in the table. There can be more values than there
179 // are actual rows; see bug 11863 for an example.
180 m_numRows = 0;
181 UT_uint32 curRowProp = 0;
182 ok = pAP->getProperty("table-row-heights", pValue);
183 if (ok && pValue != NULL) {
184 pVar = pValue;
185 while (*pVar != 0) {
186 if (*pVar == '/') {
187 if (!buffer.empty()) {
188 curRowProp++;
189 UT_UTF8String_sprintf(styleName, "%s.row%u",
190 m_tableName.utf8_str(), curRowProp);
191
192 pStyle = m_rAutomatiStyles.addTableRowStyle(styleName);
193 // Row heights values in AbiWord really are *minimum* row
194 // heights; the property name is unfortunate.
195 pStyle->setMinRowHeight(buffer.c_str());
196
197 rowStyleNames.addItem(new UT_UTF8String(styleName));
198
199 buffer.clear(); // Clear the buffer.
200 } else {
201 rowStyleNames.addItem(new UT_UTF8String(""));
202 }
203 } else {
204 buffer += *pVar;
205 }
206 pVar++;
207 }
208 }
209 }
210
211
212 /**
213 *
214 */
closeTable(ODe_ListenerAction & rAction)215 void ODe_Table_Listener::closeTable(ODe_ListenerAction& rAction) {
216 UT_sint32 i;
217 UT_UTF8String output;
218
219 _buildTable();
220
221 _printSpacesOffset(output);
222 output += "<table:table table:name=\"";
223 output += m_tableName;
224 output += "\"";
225
226 ODe_writeAttribute(output, "table:style-name", m_tableStyleName);
227 output += ">\n";
228
229 ODe_writeUTF8String(m_pTextOutput, output);
230 m_spacesOffset++;
231
232
233 output.clear();
234 _printSpacesOffset(output);
235
236 for (i=0; i<m_numColumns; i++) {
237 m_pColumns[i].write(m_pTextOutput, output);
238 }
239
240 for (i=0; i<m_numRows; i++) {
241 m_pRows[i].write(m_pTextOutput, output);
242 }
243
244
245 output.clear();
246 m_spacesOffset--;
247 _printSpacesOffset(output);
248 output += "</table:table>\n";
249 ODe_writeUTF8String(m_pTextOutput, output);
250
251 rAction.popListenerImpl();
252 }
253
254
255 /**
256 *
257 */
openCell(const PP_AttrProp * pAP,ODe_ListenerAction & rAction)258 void ODe_Table_Listener::openCell(const PP_AttrProp* pAP,
259 ODe_ListenerAction& rAction) {
260
261 ODe_Table_Cell* pCell;
262 ODe_Text_Listener* pTextListener;
263 ODe_Style_Style* pCellStyle;
264
265 // Create the table cell.
266 pCell = new ODe_Table_Cell();
267 m_cells.addItem(pCell);
268
269 pCell->loadAbiProps(pAP);
270
271 ////
272 // Try to figure out the table dimensions.
273
274 // If this cell is in the rightmost column than we have found the number
275 // of columns. If it's not, it will do no harm.
276 if (m_numColumns < pCell->m_rightAttach) {
277 m_numColumns = pCell->m_rightAttach;
278 }
279
280 // If this cell is in the last row than we have found the number
281 // of rows. If it's not, it will do no harm.
282 if (m_numRows < pCell->m_bottomAttach) {
283 m_numRows = pCell->m_bottomAttach;
284 }
285
286
287 ////
288 // Define its style
289
290 UT_UTF8String_sprintf(pCell->m_styleName, "%s_col%u_row%u",
291 m_tableName.utf8_str(),
292 (pCell->m_leftAttach)+1,
293 (pCell->m_topAttach)+1);
294
295 pCellStyle = m_rAutomatiStyles.addTableCellStyle(pCell->m_styleName);
296
297 // First inherit various the cell style properties from the table style.
298 pCellStyle->inheritTableCellProperties(m_tableWideCellStyle);
299
300 // Then load the style properties that are specific for this cell.
301 // We don't have to check if there are any properties to export at all,
302 // because AbiWord has different default cell style properties than OpenDocument,
303 // which means we'll always have to export the styles. This will only result in
304 // writing out redundant properties when the user-selected style properties
305 // exactly match the default OpenDocument default properties; we will just ignore
306 // this case.
307 pCellStyle->fetchAttributesFromAbiCell(pAP);
308
309
310 ////
311 // Prepare to read its contents
312
313 pCell->m_pTextContent = gsf_output_memory_new ();
314 UT_ASSERT(pCell->m_pTextContent != NULL);
315
316 pTextListener = new ODe_Text_Listener(
317 m_rStyles,
318 m_rAutomatiStyles,
319 pCell->m_pTextContent,
320 m_rAuxiliaryData,
321 m_zIndex,
322 // currentOffset + <table:table> + <table:table-row> + <table:table-cell>
323 m_spacesOffset+3);
324 rAction.pushListenerImpl(pTextListener, true);
325 }
326
327
328 /**
329 *
330 */
_buildTable()331 void ODe_Table_Listener::_buildTable() {
332
333 UT_sint32 i, j;
334 ODe_Table_Cell* pCell;
335
336 UT_return_if_fail(m_numRows > 0);
337 UT_return_if_fail(m_numColumns > 0);
338
339 // Create the columns
340 m_pColumns = new ODe_Table_Column[m_numColumns];
341
342 for (i=0; (i<m_numColumns) && (i<columnStyleNames.getItemCount()); i++) {
343 if (columnStyleNames[i]) {
344 m_pColumns[i].m_styleName = *(columnStyleNames[i]);
345 }
346 }
347
348
349 // Create the rows
350 m_pRows = new ODe_Table_Row[m_numRows];
351
352
353 for (i=0; (i<m_numRows) && (i<rowStyleNames.getItemCount()); i++) {
354 if (rowStyleNames[i]) {
355 m_pRows[i].m_styleName = *(rowStyleNames[i]);
356 }
357 }
358
359
360 // Create the vectors that will hold the cells into its corresponding rows.
361 for (i=0; i<m_numRows; i++) {
362 m_pRows[i].m_ppCells = new ODe_Table_Cell*[m_numColumns];
363 m_pRows[i].m_columnCount = m_numColumns;
364
365 for (j=0; j<m_numColumns; j++) {
366 m_pRows[i].m_ppCells[j] = NULL;
367 }
368 }
369
370 // Position cells in table
371 for (i=0; i<m_cells.getItemCount(); i++) {
372 pCell = m_cells.getNthItem(i);
373
374 UT_continue_if_fail(pCell)
375 UT_continue_if_fail(pCell->m_topAttach < m_numRows);
376 UT_continue_if_fail(pCell->m_leftAttach < m_numColumns);
377
378 m_pRows[pCell->m_topAttach].m_ppCells[pCell->m_leftAttach] = pCell;
379 }
380 }
381
382
383 /**
384 *
385 */
loadAbiProps(const PP_AttrProp * pAP)386 void ODe_Table_Cell::loadAbiProps(const PP_AttrProp* pAP) {
387 const gchar* pValue = NULL;
388 bool ok = false;
389
390 ok = pAP->getProperty("left-attach", pValue);
391 if (!ok || pValue == NULL) {
392 UT_DEBUGMSG(("ODe_Table_Cell::loadAbiProps(): missing left-attach property\n"));
393 return;
394 }
395 m_leftAttach = atoi(pValue);
396
397 ok = pAP->getProperty("right-attach", pValue);
398 if (!ok || pValue == NULL) {
399 UT_DEBUGMSG(("ODe_Table_Cell::loadAbiProps(): missing right-attach property\n"));
400 return;
401 }
402 m_rightAttach = atoi(pValue);
403
404 ok = pAP->getProperty("top-attach", pValue);
405 if (!ok || pValue == NULL) {
406 UT_DEBUGMSG(("ODe_Table_Cell::loadAbiProps(): missing top-attach property\n"));
407 return;
408 }
409 m_topAttach = atoi(pValue);
410
411 ok = pAP->getProperty("bot-attach", pValue);
412 if (!ok || pValue == NULL) {
413 UT_DEBUGMSG(("ODe_Table_Cell::loadAbiProps(): missing bot-attach property\n"));
414 return;
415 }
416 m_bottomAttach = atoi(pValue);
417
418 // A few sanity checks
419 UT_ASSERT(m_leftAttach < m_rightAttach);
420 UT_ASSERT(m_topAttach < m_bottomAttach);
421
422
423 ////
424 // Define some cell properties
425
426 if (m_rightAttach - m_leftAttach > 1) {
427 UT_UTF8String_sprintf(m_numberColumnsSpanned, "%d",
428 m_rightAttach-m_leftAttach);
429 }
430
431 if (m_bottomAttach - m_topAttach > 1) {
432 UT_UTF8String_sprintf(m_numberRowsSpanned, "%d",
433 m_bottomAttach - m_topAttach);
434 }
435
436 ok = pAP->getProperty(PT_XMLID, pValue);
437 if( ok && pValue )
438 {
439 m_xmlid = pValue;
440 }
441 UT_DEBUGMSG(("ODe_Table_Cell::loadAbiProps(1) ok:%d xmlid:%s \n",ok, pValue));
442 ok = pAP->getProperty("xmlid", pValue);
443 if( ok && pValue )
444 {
445 m_xmlid = pValue;
446 }
447 UT_DEBUGMSG(("ODe_Table_Cell::loadAbiProps(2) ok:%d xmlid:%s \n",ok, pValue));
448
449
450 }
451
452
453 /**
454 *
455 */
write(GsfOutput * pTableOutput,const UT_UTF8String & rSpacesOffset)456 void ODe_Table_Cell::write(GsfOutput* pTableOutput, const UT_UTF8String& rSpacesOffset) {
457 UT_UTF8String output;
458
459 output = rSpacesOffset;
460 output += "<table:table-cell";
461 ODe_writeAttribute(output, "table:style-name", m_styleName);
462 if(m_numberColumnsSpanned.size() > 0)
463 ODe_writeAttribute(output, "table:number-columns-spanned", m_numberColumnsSpanned);
464 if(m_numberRowsSpanned.size() > 0)
465 ODe_writeAttribute(output, "table:number-rows-spanned", m_numberRowsSpanned);
466 if( !m_xmlid.empty() )
467 ODe_writeAttribute(output, "xml:id", m_xmlid);
468
469 output += ">\n";
470 ODe_writeUTF8String(pTableOutput, output);
471 gsf_output_write (pTableOutput, gsf_output_size (m_pTextContent),
472 gsf_output_memory_get_bytes (GSF_OUTPUT_MEMORY (m_pTextContent)));
473
474 output = rSpacesOffset;
475 output += "</table:table-cell>\n";
476 ODe_writeUTF8String(pTableOutput, output);
477 }
478
479
480 /**
481 *
482 */
write(GsfOutput * pTableOutput,const UT_UTF8String & rSpacesOffset)483 void ODe_Table_Column::write(GsfOutput* pTableOutput, const UT_UTF8String& rSpacesOffset) {
484 UT_UTF8String output;
485
486 output = rSpacesOffset;
487 output += "<table:table-column";
488 ODe_writeAttribute(output, "table:style-name", m_styleName);
489 output += "/>\n";
490
491 ODe_writeUTF8String(pTableOutput, output);
492 }
493
494
495 /**
496 *
497 */
write(GsfOutput * pTableOutput,const UT_UTF8String & rSpacesOffset)498 void ODe_Table_Row::write(GsfOutput* pTableOutput, const UT_UTF8String& rSpacesOffset) {
499 UT_UTF8String output;
500 UT_UTF8String cellsOffset;
501 UT_uint32 i;
502
503 output = rSpacesOffset;
504 output += "<table:table-row";
505 ODe_writeAttribute(output, "table:style-name", m_styleName);
506 output += ">\n";
507
508 ODe_writeUTF8String(pTableOutput, output);
509
510 cellsOffset = rSpacesOffset;
511 cellsOffset += " ";
512
513 for (i=0; i<m_columnCount; i++) {
514 if (m_ppCells[i] != NULL) {
515 m_ppCells[i]->write(pTableOutput, cellsOffset);
516 } else {
517 // It's a covered cell.
518 output = cellsOffset;
519 output += "<table:covered-table-cell/>\n";
520 ODe_writeUTF8String(pTableOutput, output);
521 }
522 }
523
524 output = rSpacesOffset;
525 output += "</table:table-row>\n";
526 ODe_writeUTF8String(pTableOutput, output);
527 }
528
529
530 /**
531 *
532 */
ODe_Table_Row()533 ODe_Table_Row::ODe_Table_Row()
534 : m_ppCells(NULL),
535 m_columnCount(0)
536 {
537 }
538
539
540 /**
541 *
542 */
~ODe_Table_Row()543 ODe_Table_Row::~ODe_Table_Row() {
544 DELETEPV(m_ppCells);
545 }
546