1 /* AbiSource Program Utilities
2  *
3  * Copyright (C) 2005 Daniel d'Andrada T. de Carvalho
4  * <daniel.carvalho@indt.org.br>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19  * 02110-1301 USA.
20  */
21 
22 // Class definition include
23 #include "ODi_ListLevelStyle.h"
24 
25 // Internal includes
26 #include "ODi_ListenerStateAction.h"
27 #include "ODi_Style_Style.h"
28 #include "ODi_ListLevelStyleFormats.h"
29 
30 // AbiWord includes
31 #include <pd_Document.h>
32 #include <fp_types.h>
33 #include <ut_string_class.h>
34 #include <ut_units.h>
35 #include <ut_locale.h>
36 #include <ut_std_string.h>
37 
38 //External includes
39 #include <stdlib.h>
40 
41 
42 /**
43  * Constructor
44  */
ODi_ListLevelStyle(const char * pStateName,ODi_ElementStack & rElementStack)45 ODi_ListLevelStyle::ODi_ListLevelStyle(const char* pStateName,
46 									 ODi_ElementStack& rElementStack) :
47                         ODi_ListenerState(pStateName, rElementStack),
48                         m_pTextStyle(NULL)
49 {
50 }
51 
52 
53 /**
54  *
55  */
startElement(const gchar * pName,const gchar ** ppAtts,ODi_ListenerStateAction &)56 void ODi_ListLevelStyle::startElement (const gchar* pName,
57                                       const gchar** ppAtts,
58 									   ODi_ListenerStateAction& /*rAction*/)
59 {
60     const gchar* pVal = NULL;
61 
62     if (!strcmp("text:list-level-style-bullet", pName) ||
63         !strcmp("text:list-level-style-number", pName) ||
64 	!strcmp("text:outline-level-style",pName) ||
65         !strcmp("text:list-level-style-image", pName)) {
66 
67         UT_uint32 result = 0;
68 
69         pVal = UT_getAttribute ("text:level", ppAtts);
70         if (pVal) {
71             result = sscanf(pVal, "%u", &m_levelNumber);
72 	    if (result != 1) {
73 	      m_levelNumber = 1;
74 	    }
75 	    m_level = pVal;
76         } else {
77             UT_DEBUGMSG(("ODi_ListLevelStyle::startElement: missing text:level attribute\n"));
78         }
79 	bool bHeading = false;
80 	if(!strcmp("text:outline-level-style",pName))
81 	{
82 	  bHeading = true;
83 	}
84         pVal = UT_getAttribute ("text:style-name", ppAtts);
85         if (pVal)
86 	{
87             m_textStyleName = pVal;
88         }
89         else if(bHeading)
90 	{
91 	    std::string sStyleName = "BaseHeading ";
92 	    sStyleName += m_level;
93 	    m_textStyleName =  sStyleName;
94 	    xxx_UT_DEBUGMSG(("Outline List level Style name %s \n",sStyleName.utf8_str()));
95 	     pVal = UT_getAttribute ("style:num-format", ppAtts);
96 	     if(pVal && *pVal)
97 	     {
98 		 if (pVal && !strcmp(pVal, ""))
99 		 {
100 		     // We have an empty number format.
101 
102 		     // Empty list label or "invisible" list.
103 		     m_abiListListDelim = "";
104 		 }
105 
106 	     }
107 	}
108     } else if (!strcmp("style:list-level-properties", pName) ||
109                !strcmp("style:list-level-label-alignment", pName)) {
110 
111         pVal = UT_getAttribute ("text:space-before", ppAtts);
112         if (pVal) {
113             m_spaceBefore = pVal;
114         } else {
115             m_spaceBefore = "0cm";
116         }
117 
118         pVal = UT_getAttribute ("text:min-label-width", ppAtts);
119         if (pVal) {
120             m_minLabelWidth = pVal;
121         } else {
122             m_minLabelWidth = "0cm";
123         }
124 
125         pVal = UT_getAttribute ("text:min-label-distance", ppAtts);
126         if (pVal) {
127             m_minLabelDistance = pVal;
128         }
129 
130         pVal = UT_getAttribute ("fo:text-indent", ppAtts);
131         if (pVal) {
132             m_textIndent = pVal;
133         }
134 
135         pVal = UT_getAttribute ("fo:margin-left", ppAtts);
136         if (pVal) {
137             m_marginLeft = pVal;
138         }
139     }
140 }
141 
142 
143 /**
144  *
145  */
endElement(const gchar * pName,ODi_ListenerStateAction & rAction)146 void ODi_ListLevelStyle::endElement (const gchar* pName,
147                                     ODi_ListenerStateAction& rAction) {
148 
149     if (!strcmp("text:list-level-style-bullet", pName) ||
150         !strcmp("text:list-level-style-number", pName) ||
151 	!strcmp("text:outline-level-style",pName) ||
152         !strcmp("text:list-level-style-image", pName)) {
153 
154         // We're done.
155         xxx_UT_DEBUGMSG(("Finished Level %s \n",m_textStyleName.utf8_str()));
156         rAction.popState();
157     }
158 }
159 
isVisible(void) const160 bool  ODi_ListLevelStyle::isVisible(void) const
161 {
162   return  (m_abiListListDelim.size() > 0);
163 }
164 
165 /**
166  *
167  */
setAbiListID(UT_uint32 abiListID)168 void ODi_ListLevelStyle::setAbiListID(UT_uint32 abiListID) {
169     gchar buffer[100];
170 
171     sprintf(buffer, "%u", abiListID);
172     m_abiListID.assign(buffer);
173 }
174 
175 
176 /**
177  * Defines a <l> tag on the AbiWord document corresponding to this
178  * list level style.
179  */
defineAbiList(PD_Document * pDocument)180 void ODi_ListLevelStyle::defineAbiList(PD_Document* pDocument) {
181     const gchar* ppAttr[13];
182 
183     ppAttr[0] = "id";
184     ppAttr[1] = m_abiListID.c_str();
185     ppAttr[2] = "parentid";
186     ppAttr[3] = m_abiListParentID.c_str();
187     ppAttr[4] = "type";
188     ppAttr[5] = m_abiListType.c_str();
189     ppAttr[6] = "start-value";
190     ppAttr[7] = m_abiListStartValue.c_str();
191     ppAttr[8] = "list-delim";
192     ppAttr[9] = m_abiListListDelim.c_str();
193     ppAttr[10] = "list-decimal";
194     ppAttr[11] = m_abiListListDecimal.c_str();
195     ppAttr[12] = 0;
196 
197     pDocument->appendList(ppAttr);
198 }
199 
200 
201 /**
202  *
203  */
buildAbiPropsString()204 void ODi_ListLevelStyle::buildAbiPropsString() {
205     m_abiProperties.clear();
206 }
207 
208 
209 /**
210  * The AbiWord properties of the list depends on some properties already
211  * defined by the AbiWord paragraph style.
212  *
213  * @param rProps Will have the properties string appended.
214  * @param pStyle Pointer to the paragraph style used on this list paragraph.
215  */
getAbiProperties(std::string & rProps,const ODi_Style_Style * pStyle) const216 void ODi_ListLevelStyle::getAbiProperties(std::string& rProps,
217                                          const ODi_Style_Style* pStyle) const {
218 
219     // Adds the fixed portion of the properties.
220     if (!m_abiProperties.empty()) {
221         if (!rProps.empty()) {
222             rProps += "; ";
223         }
224         rProps += m_abiProperties;
225     }
226 
227     // Precedence of list styles properties:
228     //
229     // 1. The properties of the style denoted by the paragraph's style:list-style-name, overridden by
230     // 2. The properties of the paragraph's parent style, overridden by
231     // 3. The properties of the paragraph style
232 
233     std::string odMarginLeft;
234     std::string odTextIndent;
235 
236     // 1. The properties of the style denoted by the paragraph's style:list-style-name
237     if (pStyle != NULL && !pStyle->getListStyleName().empty())
238     {
239         if (!m_marginLeft.empty())
240             odMarginLeft = m_marginLeft;
241         if (!m_textIndent.empty())
242             odTextIndent = m_textIndent;
243     }
244 
245     // 2. The properties of the paragraph's parent style
246     if (pStyle != NULL && !strcmp(pStyle->getFamily()->c_str(), "paragraph")) {
247         const ODi_Style_Style* pParentStyle = pStyle->getParent();
248         if (pParentStyle != NULL && !strcmp(pParentStyle->getFamily()->c_str(), "paragraph")) {
249             if (pStyle->getMarginLeft() && !pStyle->getMarginLeft()->empty())
250                 odMarginLeft = *(pStyle->getMarginLeft());
251             if (pStyle->getTextIndent() && !pStyle->getTextIndent()->empty())
252                 odTextIndent = *(pStyle->getTextIndent());
253         }
254     }
255 
256     // 3. The properties of the paragraph style
257     if (pStyle != NULL && !strcmp(pStyle->getFamily()->c_str(), "paragraph")) {
258         if (pStyle->getMarginLeft() && !pStyle->getMarginLeft()->empty())
259             odMarginLeft = *(pStyle->getMarginLeft());
260         if (pStyle->getTextIndent() && !pStyle->getTextIndent()->empty())
261             odTextIndent = *(pStyle->getTextIndent());
262     }
263 
264     // default to some 'sane' values if not set
265     if (odMarginLeft.empty())
266         odMarginLeft = "0.0cm";
267     if (odTextIndent.empty())
268         odTextIndent = "0.0cm";
269 
270     // From the OpenDocument OASIS standard, v1.0:
271     //
272     // "The text:space-before attribute specifies the space to include before
273     // the number for all paragraphs at this level. If a paragraph has a left
274     // margin that is greater than 0, the actual position of the list label box
275     // is the left margin width plus the start indent value."
276     //
277     // AbiWord's margin-left = OpenDocument paragraph property fo:margin-left +
278     //                         OpenDocument text:space-before +
279     //                         OpenDocument text:min-label-witdh
280     //
281     // OpenDocument fo:margin-left + fo:text-indent + text:space-before = AbiWord's margin-left + text-indent.
282     //
283 
284     double spaceBefore_cm;
285     double minLabelWidth_cm;
286     double marginLeft_cm;
287     double textIndent_cm;
288 
289     gchar buffer[320];
290     UT_LocaleTransactor lt(LC_NUMERIC, "C");
291 
292     spaceBefore_cm = UT_convertToDimension(m_spaceBefore.c_str(), DIM_CM);
293     minLabelWidth_cm = UT_convertToDimension(m_minLabelWidth.c_str(), DIM_CM);
294     marginLeft_cm = UT_convertToDimension(odMarginLeft.c_str(), DIM_CM);
295     textIndent_cm = UT_convertToDimension(odTextIndent.c_str(), DIM_CM);
296 
297     double abiMarginLeft = marginLeft_cm + spaceBefore_cm + minLabelWidth_cm;
298     sprintf(buffer, "%fcm", abiMarginLeft);
299 
300     if (!rProps.empty()) {
301         rProps += "; ";
302     }
303     rProps += "margin-left:";
304     rProps.append(buffer);
305 
306     sprintf(buffer, "%fcm", marginLeft_cm + textIndent_cm + spaceBefore_cm - abiMarginLeft);
307     rProps += "; text-indent:";
308     rProps.append(buffer);
309 }
310 
311 
312 /******************************************************************************/
313 
314 
315 /**
316  *
317  */
ODi_Bullet_ListLevelStyle(ODi_ElementStack & rElementStack)318 ODi_Bullet_ListLevelStyle::ODi_Bullet_ListLevelStyle(ODi_ElementStack& rElementStack)
319 	: ODi_ListLevelStyle("Bullet_ListLevelStyle", rElementStack)
320 {
321     // Dummy values
322     m_abiListStartValue.assign("0");
323     m_abiListListDelim.assign("%L");
324     m_abiListListDecimal.assign("NULL");
325 }
326 
327 
328 /**
329  *
330  */
startElement(const gchar * pName,const gchar ** ppAtts,ODi_ListenerStateAction & rAction)331 void ODi_Bullet_ListLevelStyle::startElement(const gchar* pName,
332                                              const gchar** ppAtts,
333                                              ODi_ListenerStateAction& rAction) {
334 
335     const gchar* pVal = NULL;
336     UT_UCS4String ucs4Str;
337 
338     // Let the parent class do the processing common to all list types.
339     ODi_ListLevelStyle::startElement (pName, ppAtts, rAction);
340 
341     if (!strcmp("text:list-level-style-bullet", pName)) {
342         pVal = UT_getAttribute ("text:bullet-char", ppAtts);
343 
344         if (pVal != NULL) {
345 
346             ucs4Str = pVal;
347 
348             if (!ucs4Str.empty()) {
349                 switch (ucs4Str[0]) {
350                     case 8226: // U+2022 BULLET
351                         // Bullet List
352                         m_abiListType = UT_std_string_sprintf("%d", BULLETED_LIST);
353                         break;
354 
355                     case 8211: // U+2013 EN DASH
356                     case 8722: // U+2212 MINUS SIGN
357                         // Dashed List
358                         m_abiListType = UT_std_string_sprintf("%d", DASHED_LIST);
359                         break;
360 
361                     case 9632: // U+25A0 BLACK SQUARE
362                         // Square List
363                         m_abiListType = UT_std_string_sprintf("%d", SQUARE_LIST);
364                         break;
365 
366                     case 9650: // U+25B2 BLACK UP-POINTING TRIANGLE
367                         // Triangle List
368                         m_abiListType = UT_std_string_sprintf("%d", TRIANGLE_LIST);
369                         break;
370 
371                     case 9830: // U+2666 BLACK DIAMOND SUIT
372                         // Diamond List
373                         m_abiListType = UT_std_string_sprintf("%d", DIAMOND_LIST);
374                         break;
375 
376                     case 10035: // U+2733 EIGHT SPOKED ASTERISK
377                         // Star List
378                         m_abiListType = UT_std_string_sprintf("%d", STAR_LIST);
379                         break;
380 
381                     case 10003: // U+2713 CHECK MARK
382                         // Tick List
383                         m_abiListType = UT_std_string_sprintf("%d", TICK_LIST);
384                         break;
385 
386                     case 10066: // U+2752 UPPER RIGHT SHADOWED WHITE SQUARE
387                         // Box List
388                         m_abiListType = UT_std_string_sprintf("%d", BOX_LIST);
389                         break;
390 
391                     case 9758: // U+261E WHITE RIGHT POINTING INDEX
392                         // Hand List
393                         m_abiListType = UT_std_string_sprintf("%d", HAND_LIST);
394                         break;
395 
396                     case 9829: // U+2665 BLACK HEART SUIT
397                         // Heart List
398                         m_abiListType = UT_std_string_sprintf("%d", HEART_LIST);
399                         break;
400 
401                     case 8658: // U+21D2 RIGHTWARDS DOUBLE ARROW
402                         // Implies List
403                         m_abiListType = UT_std_string_sprintf("%d", IMPLIES_LIST);
404                         break;
405 
406                     default:
407                         // Bullet List
408                         m_abiListType = UT_std_string_sprintf("%d", BULLETED_LIST);
409                 };
410 
411             } // if (!ucs4Str.empty())
412         } else /* from if (pVal != NULL) */ {
413             // Bullet List
414             m_abiListType = UT_std_string_sprintf("%d", BULLETED_LIST);
415         }
416 
417     } else if (!strcmp("text:list-level-style-image", pName)) {
418         // Force it into a default Bullet List
419         m_abiListType = UT_std_string_sprintf("%d", BULLETED_LIST);
420     }
421 }
422 
423 
424 /**
425  *
426  */
buildAbiPropsString()427 void ODi_Bullet_ListLevelStyle::buildAbiPropsString() {
428 
429     ODi_ListLevelStyle::buildAbiPropsString();
430 
431     if (!m_abiProperties.empty()) {
432         m_abiProperties += "; ";
433     }
434 
435     m_abiProperties += "list-style:";
436     switch (atoi(m_abiListType.c_str())) {
437         case BULLETED_LIST:
438             m_abiProperties += "Bullet List;";
439             break;
440 
441         case DASHED_LIST:
442             m_abiProperties += "Dashed List;";
443             break;
444 
445         case SQUARE_LIST:
446             m_abiProperties += "Square List;";
447             break;
448 
449         case TRIANGLE_LIST:
450             m_abiProperties += "Triangle List;";
451             break;
452 
453         case DIAMOND_LIST:
454             m_abiProperties += "Diamond List;";
455             break;
456 
457         case STAR_LIST:
458             m_abiProperties += "Star List;";
459             break;
460 
461         case IMPLIES_LIST:
462             m_abiProperties += "Implies List;";
463             break;
464 
465         case TICK_LIST:
466             m_abiProperties += "Tick List;";
467             break;
468 
469         case BOX_LIST:
470             m_abiProperties += "Box List;";
471             break;
472 
473         case HAND_LIST:
474             m_abiProperties += "Hand List;";
475             break;
476 
477         case HEART_LIST:
478             m_abiProperties += "Heart List;";
479             break;
480 
481         default:
482             UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
483     }
484 
485     m_abiProperties += " field-font:NULL";
486 }
487 
488 
489 /******************************************************************************/
490 
491 
492 /**
493  * Constructor
494  */
ODi_Numbered_ListLevelStyle(ODi_ElementStack & rElementStack)495 ODi_Numbered_ListLevelStyle::ODi_Numbered_ListLevelStyle(ODi_ElementStack& rElementStack)
496     : ODi_ListLevelStyle("Numbered_ListLevelStyle", rElementStack) {
497 
498     // It seens that OpenDocument aways uses a dot "." as level delimiter.
499     m_abiListListDecimal = ".";
500     //
501     // These are default values for OpenDocument lists
502     //
503     m_abiListListDelim += "%L";
504     m_abiListStartValue = "1";
505     m_abiListType = UT_std_string_sprintf( "%d", NUMBERED_LIST);
506  }
507 
508 
509 
510 /**
511  *
512  */
startElement(const gchar * pName,const gchar ** ppAtts,ODi_ListenerStateAction & rAction)513 void ODi_Numbered_ListLevelStyle::startElement (const gchar* pName,
514                                                const gchar** ppAtts,
515                                                ODi_ListenerStateAction& rAction) {
516 
517     const gchar* pVal;
518 
519     // Let the parent class do the processing common to all list types.
520     ODi_ListLevelStyle::startElement (pName, ppAtts, rAction);
521 
522     if (!strcmp("text:list-level-style-number", pName) ||
523 	!strcmp("text:outline-level-style", pName)) {
524         std::string prefix, suffix;
525         xxx_UT_DEBUGMSG(("Doing a numbered list type %s \n",pName));
526         pVal = UT_getAttribute ("style:num-format", ppAtts);
527         UT_ASSERT_HARMLESS(pVal);
528         _setAbiListType(pVal);
529 
530         if (pVal && !strcmp(pVal, "")) {
531             // We have an empty number format.
532 
533             // Empty list label or "invisible" list.
534             m_abiListListDelim = "";
535 
536         }
537 	else {
538             // We have a number format defined.
539 
540             pVal = UT_getAttribute ("style:num-prefix", ppAtts);
541             if(pVal) {
542                 prefix = pVal;
543             }
544 
545             pVal = UT_getAttribute ("style:num-suffix", ppAtts);
546             if(pVal) {
547                 suffix = pVal;
548             }
549 
550             m_abiListListDelim  = prefix;
551             m_abiListListDelim += "%L";
552             m_abiListListDelim += suffix;
553         }
554 
555         pVal = UT_getAttribute ("text:start-value", ppAtts);
556         if(pVal) {
557             m_abiListStartValue = pVal;
558         } else {
559             // AbiWord's default value is 0, but on OpenDocument it's 1.
560             m_abiListStartValue = "1";
561         }
562     }
563 }
564 
565 
566 /**
567  *
568  */
buildAbiPropsString()569 void ODi_Numbered_ListLevelStyle::buildAbiPropsString() {
570 
571     ODi_ListLevelStyle::buildAbiPropsString();
572 
573     if (!m_abiProperties.empty()) {
574         m_abiProperties += "; ";
575     }
576 
577     m_abiProperties += "field-font: ";
578     if (m_pTextStyle) {
579         m_abiProperties += *(m_pTextStyle->getFontName());
580     } else {
581         m_abiProperties += "NULL";
582     }
583 
584     m_abiProperties += "; list-style:";
585     switch (atoi(m_abiListType.c_str())) {
586         case NUMBERED_LIST:
587             m_abiProperties += "Numbered List";
588             break;
589 
590         case LOWERCASE_LIST:
591             m_abiProperties += "Lower Case List";
592             break;
593 
594         case UPPERCASE_LIST:
595             m_abiProperties += "Upper Case List";
596             break;
597 
598         case LOWERROMAN_LIST:
599             m_abiProperties += "Lower Roman List";
600             break;
601 
602         case UPPERROMAN_LIST:
603             m_abiProperties += "Upper Roman List";
604             break;
605 
606         case ARABICNUMBERED_LIST:
607             m_abiProperties += "Arabic List";
608             break;
609 
610         default:
611             UT_ASSERT_HARMLESS(UT_SHOULD_NOT_HAPPEN);
612     }
613 }
614 
615 
616 /**
617  * Maps the value of the OpenDocument attribute style:num-format to the
618  * correspondent AbiWord "type" attribute of the list (<l>) element tag.
619  *
620  * @param pStyleNumFormat The value of the style:num-format attribute.
621  */
_setAbiListType(const gchar * pStyleNumFormat)622 void ODi_Numbered_ListLevelStyle::_setAbiListType(const gchar* pStyleNumFormat) {
623 
624     if (!pStyleNumFormat) {
625         // Use an arbitrary list type.
626         m_abiListType = UT_std_string_sprintf("%d", NUMBERED_LIST);
627 
628     } else if (!strcmp(pStyleNumFormat, "1")) {
629         m_abiListType = UT_std_string_sprintf("%d", NUMBERED_LIST);
630 
631     } else if (!strcmp(pStyleNumFormat, "a")) {
632         m_abiListType = UT_std_string_sprintf("%d", LOWERCASE_LIST);
633 
634     } else if (!strcmp(pStyleNumFormat, "A")) {
635         m_abiListType = UT_std_string_sprintf("%d", UPPERCASE_LIST);
636 
637     } else if (!strcmp(pStyleNumFormat, "i")) {
638         m_abiListType = UT_std_string_sprintf("%d", LOWERROMAN_LIST);
639 
640     } else if (!strcmp(pStyleNumFormat, "I")) {
641         m_abiListType = UT_std_string_sprintf("%d", UPPERROMAN_LIST);
642 
643     } else if (!strcmp(pStyleNumFormat, ODI_LISTLEVELSTYLE_ARABIC)) {
644         m_abiListType = UT_std_string_sprintf("%d", ARABICNUMBERED_LIST);
645 
646     } else {
647         // Use an arbitrary list type.
648         m_abiListType = UT_std_string_sprintf("%d", NUMBERED_LIST);
649     }
650 }
651