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